Skip to content

Commit

Permalink
e2e: added happy path e2e test for ICS20 authz integration (#2996)
Browse files Browse the repository at this point in the history
  • Loading branch information
chatton authored Jan 11, 2023
1 parent f9be376 commit aabb07a
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/e2e-manual-simd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ on:
- TestInterchainAccountsGroupsTestSuite
- TestInterchainAccountsGovTestSuite
- TestIncentivizedInterchainAccountsTestSuite
- TestAuthzTransferTestSuite
chain-image:
description: 'The image to use for chain A'
required: true
Expand Down
8 changes: 8 additions & 0 deletions e2e/testconfig/testconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"strings"

"github.com/cosmos/cosmos-sdk/codec"
simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
Expand Down Expand Up @@ -129,6 +130,13 @@ func GetChainBTag() string {
return chainBTag
}

// IsCI returns true if the tests are running in CI, false is returned
// if the tests are running locally.
// Note: github actions passes a CI env value of true by default to all runners.
func IsCI() bool {
return strings.ToLower(os.Getenv("CI")) == "true"
}

// ChainOptions stores chain configurations for the chains that will be
// created for the tests. They can be modified by passing ChainOptionConfiguration
// to E2ETestSuite.GetChains.
Expand Down
156 changes: 156 additions & 0 deletions e2e/tests/transfer/authz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package transfer

import (
"context"
"testing"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/authz"
test "github.com/strangelove-ventures/ibctest/v6/testutil"
"github.com/stretchr/testify/suite"

"github.com/cosmos/ibc-go/e2e/testsuite"
"github.com/cosmos/ibc-go/e2e/testvalues"
transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
)

func TestAuthzTransferTestSuite(t *testing.T) {
suite.Run(t, new(AuthzTransferTestSuite))
}

type AuthzTransferTestSuite struct {
testsuite.E2ETestSuite
}

func (suite *AuthzTransferTestSuite) TestAuthz_MsgTransfer_Succeeds() {
t := suite.T()
ctx := context.TODO()

relayer, channelA := suite.SetupChainsRelayerAndChannel(ctx, transferChannelOptions())
chainA, chainB := suite.GetChains()

chainADenom := chainA.Config().Denom

granterWallet := suite.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
granterAddress := granterWallet.Bech32Address(chainA.Config().Bech32Prefix)

granteeWallet := suite.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
granteeAddress := granteeWallet.Bech32Address(chainA.Config().Bech32Prefix)

receiverWallet := suite.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
receiverWalletAddress := receiverWallet.Bech32Address(chainB.Config().Bech32Prefix)

t.Run("start relayer", func(t *testing.T) {
suite.StartRelayer(relayer)
})

// createMsgGrantFn initializes a TransferAuthorization and broadcasts a MsgGrant message.
createMsgGrantFn := func(t *testing.T) {
transferAuth := transfertypes.TransferAuthorization{
Allocations: []transfertypes.Allocation{
{
SourcePort: channelA.PortID,
SourceChannel: channelA.ChannelID,
SpendLimit: sdk.NewCoins(sdk.NewCoin(chainADenom, sdk.NewInt(testvalues.StartingTokenAmount))),
AllowList: []string{receiverWalletAddress},
},
},
}

authAny, err := codectypes.NewAnyWithValue(&transferAuth)
suite.Require().NoError(err)

msgGrant := &authz.MsgGrant{
Granter: granterAddress,
Grantee: granteeAddress,
Grant: authz.Grant{
Authorization: authAny,
// no expiration
Expiration: nil,
},
}

resp, err := suite.BroadcastMessages(context.TODO(), chainA, granterWallet, msgGrant)
suite.AssertValidTxResponse(resp)
suite.Require().NoError(err)
}

// 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) {
return func(t *testing.T) {
grantAuths, err := suite.QueryGranterGrants(ctx, chainA, granterAddress)

suite.Require().NoError(err)
suite.Require().Len(grantAuths, 1)
grantAuthorization := grantAuths[0]

transferAuth := suite.extractTransferAuthorizationFromGrantAuthorization(grantAuthorization)
expectedSpendLimit := sdk.NewCoins(sdk.NewCoin(chainADenom, sdk.NewInt(expectedLimit)))
suite.Require().Equal(expectedSpendLimit, transferAuth.Allocations[0].SpendLimit)
}
}

t.Run("broadcast MsgGrant", createMsgGrantFn)

t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) {
transferMsg := transfertypes.MsgTransfer{
SourcePort: channelA.PortID,
SourceChannel: channelA.ChannelID,
Token: testvalues.DefaultTransferAmount(chainADenom),
Sender: granterAddress,
Receiver: receiverWalletAddress,
TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB),
}

transferAny, err := codectypes.NewAnyWithValue(&transferMsg)
suite.Require().NoError(err)

msgExec := &authz.MsgExec{
Grantee: granteeAddress,
Msgs: []*codectypes.Any{transferAny},
}

resp, err := suite.BroadcastMessages(context.TODO(), chainA, granteeWallet, msgExec)
suite.AssertValidTxResponse(resp)
suite.Require().NoError(err)
})

t.Run("verify granter wallet amount", func(t *testing.T) {
actualBalance, err := suite.GetChainANativeBalance(ctx, granterWallet)
suite.Require().NoError(err)

expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount
suite.Require().Equal(expected, actualBalance)
})

suite.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 := chainB.GetBalance(ctx, receiverWalletAddress, chainBIBCToken.IBCDenom())
suite.Require().NoError(err)
suite.Require().Equal(testvalues.IBCTransferAmount, actualBalance)
})

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))

}

// extractTransferAuthorizationFromGrantAuthorization extracts a TransferAuthorization from the given
// GrantAuthorization.
func (suite *AuthzTransferTestSuite) extractTransferAuthorizationFromGrantAuthorization(grantAuth *authz.GrantAuthorization) *transfertypes.TransferAuthorization {
cfg := testsuite.EncodingConfig()
var authorization authz.Authorization
err := cfg.InterfaceRegistry.UnpackAny(grantAuth.Authorization, &authorization)
suite.Require().NoError(err)

transferAuth, ok := authorization.(*transfertypes.TransferAuthorization)
suite.Require().True(ok)
return transferAuth
}
4 changes: 4 additions & 0 deletions e2e/testsuite/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package testsuite
import (
"github.com/cosmos/cosmos-sdk/codec"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/authz"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"

transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
simappparams "github.com/cosmos/ibc-go/v6/testing/simapp/params"
)

Expand All @@ -24,6 +26,8 @@ func codecAndEncodingConfig() (*codec.ProtoCodec, simappparams.EncodingConfig) {
banktypes.RegisterInterfaces(cfg.InterfaceRegistry)
govv1beta1.RegisterInterfaces(cfg.InterfaceRegistry)
authtypes.RegisterInterfaces(cfg.InterfaceRegistry)
authz.RegisterInterfaces(cfg.InterfaceRegistry)
transfertypes.RegisterInterfaces(cfg.InterfaceRegistry)
cdc := codec.NewProtoCodec(cfg.InterfaceRegistry)
return cdc, cfg
}
30 changes: 29 additions & 1 deletion e2e/testsuite/testsuite.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/authz"
govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
grouptypes "github.com/cosmos/cosmos-sdk/x/group"
Expand Down Expand Up @@ -81,6 +82,7 @@ type GRPCClients struct {
GroupsQueryClient grouptypes.QueryClient
ParamsQueryClient paramsproposaltypes.QueryClient
AuthQueryClient authtypes.QueryClient
AuthZQueryClient authz.QueryClient
}

// path is a pairing of two chains which will be used in a test.
Expand Down Expand Up @@ -391,6 +393,7 @@ func (s *E2ETestSuite) InitGRPCClients(chain *cosmos.CosmosChain) {
GroupsQueryClient: grouptypes.NewQueryClient(grpcConn),
ParamsQueryClient: paramsproposaltypes.NewQueryClient(grpcConn),
AuthQueryClient: authtypes.NewQueryClient(grpcConn),
AuthZQueryClient: authz.NewQueryClient(grpcConn),
}
}

Expand Down Expand Up @@ -425,7 +428,7 @@ func (s *E2ETestSuite) createCosmosChains(chainOptions testconfig.ChainOptions)

logger := zaptest.NewLogger(s.T())

numValidators, numFullNodes := 4, 1
numValidators, numFullNodes := getValidatorsAndFullNodes()

chainA := cosmos.NewCosmosChain(s.T().Name(), *chainOptions.ChainAConfig, numValidators, numFullNodes, logger)
chainB := cosmos.NewCosmosChain(s.T().Name(), *chainOptions.ChainBConfig, numValidators, numFullNodes, logger)
Expand Down Expand Up @@ -539,7 +542,32 @@ func (s *E2ETestSuite) QueryModuleAccountAddress(ctx context.Context, moduleName
return moduleAccount.GetAddress(), nil
}

// QueryGranterGrants returns all GrantAuthorizations for the given granterAddress.
func (s *E2ETestSuite) QueryGranterGrants(ctx context.Context, chain *cosmos.CosmosChain, granterAddress string) ([]*authz.GrantAuthorization, error) {
authzClient := s.GetChainGRCPClients(chain).AuthZQueryClient
queryRequest := &authz.QueryGranterGrantsRequest{
Granter: granterAddress,
}

grants, err := authzClient.GranterGrants(ctx, queryRequest)
if err != nil {
return nil, err
}

return grants.Grants, nil
}

// GetIBCToken returns the denomination of the full token denom sent to the receiving channel
func GetIBCToken(fullTokenDenom string, portID, channelID string) transfertypes.DenomTrace {
return transfertypes.ParseDenomTrace(fmt.Sprintf("%s/%s/%s", portID, channelID, fullTokenDenom))
}

// getValidatorsAndFullNodes returns the number of validators and full nodes respectively that should be used for
// the test. If the test is running in CI, more nodes are used, when running locally a single node is used to
// use less resources and allow the tests to run faster.
func getValidatorsAndFullNodes() (int, int) {
if testconfig.IsCI() {
return 4, 1
}
return 1, 0
}

0 comments on commit aabb07a

Please sign in to comment.