Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
379 lines
12 KiB
Go
379 lines
12 KiB
Go
//go:build !test_e2e
|
|
|
|
package transfer
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/cosmos/interchaintest/v10/ibc"
|
|
test "github.com/cosmos/interchaintest/v10/testutil"
|
|
testifysuite "github.com/stretchr/testify/suite"
|
|
|
|
sdkmath "cosmossdk.io/math"
|
|
|
|
codectypes "git.cw.tr/mukan-network/mukan-sdk/codec/types"
|
|
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
|
|
sdkerrors "git.cw.tr/mukan-network/mukan-sdk/types/errors"
|
|
"git.cw.tr/mukan-network/mukan-sdk/x/authz"
|
|
|
|
"github.com/cosmos/ibc-go/e2e/testsuite"
|
|
"github.com/cosmos/ibc-go/e2e/testsuite/query"
|
|
"github.com/cosmos/ibc-go/e2e/testvalues"
|
|
transfertypes "git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types"
|
|
ibcerrors "git.cw.tr/mukan-network/mukan-ibc/modules/core/errors"
|
|
)
|
|
|
|
// compatibility:from_version: v7.10.0
|
|
func TestAuthzTransferTestSuite(t *testing.T) {
|
|
testifysuite.Run(t, new(AuthzTransferTestSuite))
|
|
}
|
|
|
|
type AuthzTransferTestSuite struct {
|
|
testsuite.E2ETestSuite
|
|
}
|
|
|
|
// SetupSuite sets up chains for the current test suite
|
|
func (s *AuthzTransferTestSuite) SetupSuite() {
|
|
s.SetupChains(context.TODO(), 2, nil)
|
|
}
|
|
|
|
func (s *AuthzTransferTestSuite) CreateAuthzTestPath(testName string) (ibc.Relayer, ibc.ChannelOutput) {
|
|
return s.CreatePaths(ibc.DefaultClientOpts(), s.TransferChannelOptions(), testName), s.GetChainAChannelForTest(testName)
|
|
}
|
|
|
|
// QueryGranterGrants returns all GrantAuthorizations for the given granterAddress.
|
|
func (*AuthzTransferTestSuite) QueryGranterGrants(ctx context.Context, chain ibc.Chain, granterAddress string) ([]*authz.GrantAuthorization, error) {
|
|
res, err := query.GRPCQuery[authz.QueryGranterGrantsResponse](ctx, chain, &authz.QueryGranterGrantsRequest{
|
|
Granter: granterAddress,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return res.Grants, nil
|
|
}
|
|
|
|
func (s *AuthzTransferTestSuite) TestAuthz_MsgTransfer_Succeeds() {
|
|
t := s.T()
|
|
ctx := context.TODO()
|
|
|
|
testName := t.Name()
|
|
t.Parallel()
|
|
relayer, channelA := s.CreateAuthzTestPath(testName)
|
|
|
|
chainA, chainB := s.GetChains()
|
|
chainADenom := chainA.Config().Denom
|
|
|
|
granterWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
|
|
granterAddress := granterWallet.FormattedAddress()
|
|
|
|
granteeWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
|
|
granteeAddress := granteeWallet.FormattedAddress()
|
|
|
|
receiverWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
|
|
receiverWalletAddress := receiverWallet.FormattedAddress()
|
|
|
|
t.Run("start relayer", func(t *testing.T) {
|
|
s.StartRelayer(relayer, testName)
|
|
})
|
|
|
|
// createMsgGrantFn initializes a TransferAuthorization and broadcasts a MsgGrant message.
|
|
createMsgGrantFn := func(t *testing.T) {
|
|
t.Helper()
|
|
transferAuth := transfertypes.TransferAuthorization{
|
|
Allocations: []transfertypes.Allocation{
|
|
{
|
|
SourcePort: channelA.PortID,
|
|
SourceChannel: channelA.ChannelID,
|
|
SpendLimit: sdk.NewCoins(sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.StartingTokenAmount))),
|
|
AllowList: []string{receiverWalletAddress},
|
|
},
|
|
},
|
|
}
|
|
|
|
protoAny, err := codectypes.NewAnyWithValue(&transferAuth)
|
|
s.Require().NoError(err)
|
|
|
|
msgGrant := &authz.MsgGrant{
|
|
Granter: granterAddress,
|
|
Grantee: granteeAddress,
|
|
Grant: authz.Grant{
|
|
Authorization: protoAny,
|
|
// no expiration
|
|
Expiration: nil,
|
|
},
|
|
}
|
|
|
|
resp := s.BroadcastMessages(context.TODO(), chainA, granterWallet, msgGrant)
|
|
s.AssertTxSuccess(resp)
|
|
}
|
|
|
|
// verifyGrantFn returns a test function which asserts chainA has a grant authorization
|
|
// with the given spend limit.
|
|
verifyGrantFn := func(expectedLimit int64) func(t *testing.T) {
|
|
t.Helper()
|
|
return func(t *testing.T) {
|
|
t.Helper()
|
|
grantAuths, err := s.QueryGranterGrants(ctx, chainA, granterAddress)
|
|
|
|
s.Require().NoError(err)
|
|
s.Require().Len(grantAuths, 1)
|
|
grantAuthorization := grantAuths[0]
|
|
|
|
transferAuth := s.extractTransferAuthorizationFromGrantAuthorization(grantAuthorization)
|
|
expectedSpendLimit := sdk.NewCoins(sdk.NewCoin(chainADenom, sdkmath.NewInt(expectedLimit)))
|
|
s.Require().Equal(expectedSpendLimit, transferAuth.Allocations[0].SpendLimit)
|
|
}
|
|
}
|
|
|
|
t.Run("broadcast MsgGrant", createMsgGrantFn)
|
|
|
|
t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) {
|
|
transferMsg := testsuite.GetMsgTransfer(
|
|
channelA.PortID,
|
|
channelA.ChannelID,
|
|
channelA.Version,
|
|
testvalues.DefaultTransferAmount(chainADenom),
|
|
granterAddress,
|
|
receiverWalletAddress,
|
|
s.GetTimeoutHeight(ctx, chainB),
|
|
0,
|
|
"",
|
|
)
|
|
|
|
protoAny, err := codectypes.NewAnyWithValue(transferMsg)
|
|
s.Require().NoError(err)
|
|
|
|
msgExec := &authz.MsgExec{
|
|
Grantee: granteeAddress,
|
|
Msgs: []*codectypes.Any{protoAny},
|
|
}
|
|
|
|
resp := s.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec)
|
|
s.AssertTxSuccess(resp)
|
|
})
|
|
|
|
t.Run("verify granter wallet amount", func(t *testing.T) {
|
|
actualBalance, err := s.GetChainANativeBalance(ctx, granterWallet)
|
|
s.Require().NoError(err)
|
|
|
|
expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount
|
|
s.Require().Equal(expected, actualBalance)
|
|
})
|
|
|
|
s.Require().NoError(test.WaitForBlocks(context.TODO(), 10, chainB))
|
|
|
|
t.Run("verify receiver wallet amount", func(t *testing.T) {
|
|
chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID)
|
|
actualBalance, err := query.Balance(ctx, chainB, receiverWalletAddress, chainBIBCToken.IBCDenom())
|
|
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(testvalues.IBCTransferAmount, actualBalance.Int64())
|
|
})
|
|
|
|
t.Run("granter grant spend limit reduced", verifyGrantFn(testvalues.StartingTokenAmount-testvalues.IBCTransferAmount))
|
|
|
|
t.Run("re-initialize MsgGrant", createMsgGrantFn)
|
|
|
|
t.Run("granter grant was reinitialized", verifyGrantFn(testvalues.StartingTokenAmount))
|
|
|
|
t.Run("revoke access", func(t *testing.T) {
|
|
msgRevoke := authz.MsgRevoke{
|
|
Granter: granterAddress,
|
|
Grantee: granteeAddress,
|
|
MsgTypeUrl: transfertypes.TransferAuthorization{}.MsgTypeURL(),
|
|
}
|
|
|
|
resp := s.BroadcastMessages(context.TODO(), chainA, granterWallet, &msgRevoke)
|
|
s.AssertTxSuccess(resp)
|
|
})
|
|
|
|
t.Run("exec unauthorized MsgTransfer", func(t *testing.T) {
|
|
transferMsg := testsuite.GetMsgTransfer(
|
|
channelA.PortID,
|
|
channelA.ChannelID,
|
|
channelA.Version,
|
|
testvalues.DefaultTransferAmount(chainADenom),
|
|
granterAddress,
|
|
receiverWalletAddress,
|
|
s.GetTimeoutHeight(ctx, chainB),
|
|
0,
|
|
"",
|
|
)
|
|
|
|
protoAny, err := codectypes.NewAnyWithValue(transferMsg)
|
|
s.Require().NoError(err)
|
|
|
|
msgExec := &authz.MsgExec{
|
|
Grantee: granteeAddress,
|
|
Msgs: []*codectypes.Any{protoAny},
|
|
}
|
|
|
|
resp := s.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec)
|
|
s.AssertTxFailure(resp, authz.ErrNoAuthorizationFound)
|
|
})
|
|
}
|
|
|
|
func (s *AuthzTransferTestSuite) TestAuthz_InvalidTransferAuthorizations() {
|
|
t := s.T()
|
|
ctx := context.TODO()
|
|
|
|
testName := t.Name()
|
|
t.Parallel()
|
|
relayer, channelA := s.CreateAuthzTestPath(testName)
|
|
|
|
chainA, chainB := s.GetChains()
|
|
chainADenom := chainA.Config().Denom
|
|
chainAVersion := chainA.Config().Images[0].Version
|
|
|
|
granterWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
|
|
granterAddress := granterWallet.FormattedAddress()
|
|
|
|
granteeWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
|
|
granteeAddress := granteeWallet.FormattedAddress()
|
|
|
|
receiverWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
|
|
receiverWalletAddress := receiverWallet.FormattedAddress()
|
|
|
|
t.Run("start relayer", func(t *testing.T) {
|
|
s.StartRelayer(relayer, testName)
|
|
})
|
|
|
|
const spendLimit = 1000
|
|
|
|
t.Run("broadcast MsgGrant", func(t *testing.T) {
|
|
transferAuth := transfertypes.TransferAuthorization{
|
|
Allocations: []transfertypes.Allocation{
|
|
{
|
|
SourcePort: channelA.PortID,
|
|
SourceChannel: channelA.ChannelID,
|
|
SpendLimit: sdk.NewCoins(sdk.NewCoin(chainADenom, sdkmath.NewInt(spendLimit))),
|
|
AllowList: []string{receiverWalletAddress},
|
|
},
|
|
},
|
|
}
|
|
|
|
protoAny, err := codectypes.NewAnyWithValue(&transferAuth)
|
|
s.Require().NoError(err)
|
|
|
|
msgGrant := &authz.MsgGrant{
|
|
Granter: granterAddress,
|
|
Grantee: granteeAddress,
|
|
Grant: authz.Grant{
|
|
Authorization: protoAny,
|
|
// no expiration
|
|
Expiration: nil,
|
|
},
|
|
}
|
|
|
|
resp := s.BroadcastMessages(context.TODO(), chainA, granterWallet, msgGrant)
|
|
s.AssertTxSuccess(resp)
|
|
})
|
|
|
|
t.Run("exceed spend limit", func(t *testing.T) {
|
|
const invalidSpendAmount = spendLimit + 1
|
|
|
|
t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) {
|
|
transferMsg := testsuite.GetMsgTransfer(
|
|
channelA.PortID,
|
|
channelA.ChannelID,
|
|
channelA.Version,
|
|
sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(invalidSpendAmount)},
|
|
granterAddress,
|
|
receiverWalletAddress,
|
|
s.GetTimeoutHeight(ctx, chainB),
|
|
0,
|
|
"",
|
|
)
|
|
|
|
protoAny, err := codectypes.NewAnyWithValue(transferMsg)
|
|
s.Require().NoError(err)
|
|
|
|
msgExec := &authz.MsgExec{
|
|
Grantee: granteeAddress,
|
|
Msgs: []*codectypes.Any{protoAny},
|
|
}
|
|
|
|
resp := s.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec)
|
|
if testvalues.IbcErrorsFeatureReleases.IsSupported(chainAVersion) {
|
|
s.AssertTxFailure(resp, ibcerrors.ErrInsufficientFunds)
|
|
} else {
|
|
s.AssertTxFailure(resp, sdkerrors.ErrInsufficientFunds)
|
|
}
|
|
})
|
|
|
|
t.Run("verify granter wallet amount", func(t *testing.T) {
|
|
actualBalance, err := s.GetChainANativeBalance(ctx, granterWallet)
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(testvalues.StartingTokenAmount, actualBalance)
|
|
})
|
|
|
|
t.Run("verify receiver wallet amount", func(t *testing.T) {
|
|
chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID)
|
|
actualBalance, err := query.Balance(ctx, chainB, receiverWalletAddress, chainBIBCToken.IBCDenom())
|
|
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(int64(0), actualBalance.Int64())
|
|
})
|
|
|
|
t.Run("granter grant spend limit unchanged", func(t *testing.T) {
|
|
grantAuths, err := s.QueryGranterGrants(ctx, chainA, granterAddress)
|
|
|
|
s.Require().NoError(err)
|
|
s.Require().Len(grantAuths, 1)
|
|
grantAuthorization := grantAuths[0]
|
|
|
|
transferAuth := s.extractTransferAuthorizationFromGrantAuthorization(grantAuthorization)
|
|
expectedSpendLimit := sdk.NewCoins(sdk.NewCoin(chainADenom, sdkmath.NewInt(spendLimit)))
|
|
s.Require().Equal(expectedSpendLimit, transferAuth.Allocations[0].SpendLimit)
|
|
})
|
|
})
|
|
|
|
t.Run("send funds to invalid address", func(t *testing.T) {
|
|
invalidWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
|
|
invalidWalletAddress := invalidWallet.FormattedAddress()
|
|
|
|
t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) {
|
|
transferMsg := testsuite.GetMsgTransfer(
|
|
channelA.PortID,
|
|
channelA.ChannelID,
|
|
channelA.Version,
|
|
sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(spendLimit)},
|
|
granterAddress,
|
|
invalidWalletAddress,
|
|
s.GetTimeoutHeight(ctx, chainB),
|
|
0,
|
|
"",
|
|
)
|
|
|
|
protoAny, err := codectypes.NewAnyWithValue(transferMsg)
|
|
s.Require().NoError(err)
|
|
|
|
msgExec := &authz.MsgExec{
|
|
Grantee: granteeAddress,
|
|
Msgs: []*codectypes.Any{protoAny},
|
|
}
|
|
|
|
resp := s.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec)
|
|
if testvalues.IbcErrorsFeatureReleases.IsSupported(chainAVersion) {
|
|
s.AssertTxFailure(resp, ibcerrors.ErrInvalidAddress)
|
|
} else {
|
|
s.AssertTxFailure(resp, sdkerrors.ErrInvalidAddress)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// extractTransferAuthorizationFromGrantAuthorization extracts a TransferAuthorization from the given
|
|
// GrantAuthorization.
|
|
func (s *AuthzTransferTestSuite) extractTransferAuthorizationFromGrantAuthorization(grantAuth *authz.GrantAuthorization) *transfertypes.TransferAuthorization {
|
|
cfg := testsuite.SDKEncodingConfig()
|
|
var authorization authz.Authorization
|
|
err := cfg.InterfaceRegistry.UnpackAny(grantAuth.Authorization, &authorization)
|
|
s.Require().NoError(err)
|
|
|
|
transferAuth, ok := authorization.(*transfertypes.TransferAuthorization)
|
|
s.Require().True(ok)
|
|
return transferAuth
|
|
}
|