Skip to content

Commit

Permalink
feat: make a state entry to find total amount of native assets IBC'd …
Browse files Browse the repository at this point in the history
…out (#3019)
  • Loading branch information
stackman27 committed Apr 22, 2023
1 parent 295bab0 commit fa9418f
Show file tree
Hide file tree
Showing 28 changed files with 1,840 additions and 170 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/e2e-upgrade.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,5 @@ jobs:
chain-upgrade-tag: pr-3164 # TODO: needs v7.1.0 when cut
upgrade-plan-name: "v7.1"
test-entry-point: "TestUpgradeTestSuite"
test: "TestV7ChainUpgradeAddLocalhost"
test: "TestV7ToV7_1ChainUpgrade"
upload-logs: true
2 changes: 1 addition & 1 deletion e2e/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/cosmos/ibc-go/e2e
go 1.19

require (
cosmossdk.io/math v1.0.0
github.com/cometbft/cometbft v0.37.0
github.com/cosmos/cosmos-sdk v0.47.0
github.com/cosmos/gogoproto v1.4.8
Expand All @@ -27,7 +28,6 @@ require (
cosmossdk.io/core v0.6.0 // indirect
cosmossdk.io/depinject v1.0.0-alpha.3 // indirect
cosmossdk.io/errors v1.0.0-beta.7 // indirect
cosmossdk.io/math v1.0.0 // indirect
cosmossdk.io/tools/rosetta v0.2.1 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
Expand Down
17 changes: 17 additions & 0 deletions e2e/tests/transfer/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"
"time"

// "cosmossdk.io/math"
paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal"
"github.com/strangelove-ventures/interchaintest/v7/ibc"
test "github.com/strangelove-ventures/interchaintest/v7/testutil"
Expand Down Expand Up @@ -88,6 +89,12 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() {

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

// actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom)
// s.Require().NoError(err)

// expectedTotalEscrow := math.NewInt(testvalues.IBCTransferAmount)
// s.Require().Equal(expectedTotalEscrow, actualTotalEscrow)
})

t.Run("start relayer", func(t *testing.T) {
Expand Down Expand Up @@ -117,6 +124,10 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() {
s.Require().NoError(err)

s.Require().Equal(int64(0), actualBalance)

// actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainB, chainBIBCToken.IBCDenom())
// s.Require().NoError(err)
// s.Require().Equal(math.ZeroInt(), actualTotalEscrow) // total escrow is zero because sending chain is not source for tokens
})

s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks")
Expand All @@ -130,6 +141,12 @@ func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() {
expected := testvalues.StartingTokenAmount
s.Require().Equal(expected, actualBalance)
})

// t.Run("tokens are un-escrowed", func(t *testing.T) {
// actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom)
// s.Require().NoError(err)
// s.Require().Equal(math.ZeroInt(), actualTotalEscrow) // total escrow is zero because tokens have come back
// })
}

// TestMsgTransfer_Fails_InvalidAddress attempts to send an IBC transfer to an invalid address and ensures
Expand Down
68 changes: 61 additions & 7 deletions e2e/tests/upgrades/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"
"time"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
Expand Down Expand Up @@ -605,33 +606,86 @@ func (s *UpgradeTestSuite) TestV6ToV7ChainUpgrade() {
})
}

func (s *UpgradeTestSuite) TestV7ChainUpgradeAddLocalhost() {
func (s *UpgradeTestSuite) TestV7ToV7_1ChainUpgrade() {
t := s.T()
testCfg := testconfig.LoadConfig()

ctx := context.Background()
_, _ = s.SetupChainsRelayerAndChannel(ctx)
chain, _ := s.GetChains()
relayer, channelA := s.SetupChainsRelayerAndChannel(ctx)
chainA, chainB := s.GetChains()

chainADenom := chainA.Config().Denom

chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainAAddress := chainAWallet.FormattedAddress()

chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
chainBAddress := chainBWallet.FormattedAddress()

s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks")

t.Run("transfer native tokens from chainA to chainB", func(t *testing.T) {
transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
s.Require().NoError(err)
s.AssertValidTxResponse(transferTxResp)
})

t.Run("tokens are escrowed", func(t *testing.T) {
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
s.Require().NoError(err)

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

// Escrow amount for native denom is not stored in state because pre-upgrade version did not support this feature
actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom)
s.Require().NoError(err)
s.Require().Equal(math.ZeroInt(), actualTotalEscrow)
})

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

chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID)

t.Run("packet is relayed", func(t *testing.T) {
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)

s.Require().NoError(test.WaitForBlocks(ctx, 5, chain), "failed to wait for blocks")
actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom())
s.Require().NoError(err)

expected := testvalues.IBCTransferAmount
s.Require().Equal(expected, actualBalance)
})

s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "failed to wait for blocks")

t.Run("upgrade chain", func(t *testing.T) {
govProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
s.UpgradeChain(ctx, chain, govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag)
s.UpgradeChain(ctx, chainA, govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag)
})

t.Run("ensure the localhost client is active and sentinel connection is stored in state", func(t *testing.T) {
status, err := s.QueryClientStatus(ctx, chain, exported.LocalhostClientID)
status, err := s.QueryClientStatus(ctx, chainA, exported.LocalhostClientID)
s.Require().NoError(err)
s.Require().Equal(exported.Active.String(), status)

connectionEnd, err := s.QueryConnection(ctx, chain, exported.LocalhostConnectionID)
connectionEnd, err := s.QueryConnection(ctx, chainA, exported.LocalhostConnectionID)
s.Require().NoError(err)
s.Require().Equal(connectiontypes.OPEN, connectionEnd.State)
s.Require().Equal(exported.LocalhostClientID, connectionEnd.ClientId)
s.Require().Equal(exported.LocalhostClientID, connectionEnd.Counterparty.ClientId)
s.Require().Equal(exported.LocalhostConnectionID, connectionEnd.Counterparty.ConnectionId)
})

t.Run("ensure escrow amount for native denom is stored in state", func(t *testing.T) {
actualTotalEscrow, err := s.QueryTotalEscrowForDenom(ctx, chainA, chainADenom)
s.Require().NoError(err)

expectedTotalEscrow := math.NewInt(testvalues.IBCTransferAmount)
s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) // migration has run and total escrow amount has been set
})
}

// RegisterInterchainAccount will attempt to register an interchain account on the counterparty chain.
Expand Down
17 changes: 17 additions & 0 deletions e2e/testsuite/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"sort"

"cosmossdk.io/math"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cosmos/cosmos-sdk/client/grpc/tmservice"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -22,6 +23,7 @@ import (

controllertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types"
feetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types"
transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"
connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types"
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
Expand All @@ -35,6 +37,7 @@ type GRPCClients struct {
ClientQueryClient clienttypes.QueryClient
ConnectionQueryClient connectiontypes.QueryClient
ChannelQueryClient channeltypes.QueryClient
TransferQueryClient transfertypes.QueryClient
FeeQueryClient feetypes.QueryClient
ICAQueryClient controllertypes.QueryClient
InterTxQueryClient intertxtypes.QueryClient
Expand Down Expand Up @@ -72,6 +75,7 @@ func (s *E2ETestSuite) InitGRPCClients(chain *cosmos.CosmosChain) {
s.grpcClients[chain.Config().ChainID] = GRPCClients{
ClientQueryClient: clienttypes.NewQueryClient(grpcConn),
ChannelQueryClient: channeltypes.NewQueryClient(grpcConn),
TransferQueryClient: transfertypes.NewQueryClient(grpcConn),
FeeQueryClient: feetypes.NewQueryClient(grpcConn),
ICAQueryClient: controllertypes.NewQueryClient(grpcConn),
InterTxQueryClient: intertxtypes.NewQueryClient(grpcConn),
Expand Down Expand Up @@ -158,6 +162,19 @@ func (s *E2ETestSuite) QueryPacketCommitment(ctx context.Context, chain ibc.Chai
return res.Commitment, nil
}

// QueryTotalEscrowForDenom queries the total amount of tokens in escrow for a denom
func (s *E2ETestSuite) QueryTotalEscrowForDenom(ctx context.Context, chain ibc.Chain, denom string) (math.Int, error) {
queryClient := s.GetChainGRCPClients(chain).TransferQueryClient
res, err := queryClient.TotalEscrowForDenom(ctx, &transfertypes.QueryTotalEscrowForDenomRequest{
Denom: denom,
})
if err != nil {
return math.ZeroInt(), err
}

return res.Amount, nil
}

// QueryInterchainAccount queries the interchain account for the given owner and connectionID.
func (s *E2ETestSuite) QueryInterchainAccount(ctx context.Context, chain ibc.Chain, owner, connectionID string) (string, error) {
queryClient := s.GetChainGRCPClients(chain).ICAQueryClient
Expand Down
1 change: 1 addition & 0 deletions modules/apps/transfer/client/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func GetQueryCmd() *cobra.Command {
GetCmdParams(),
GetCmdQueryEscrowAddress(),
GetCmdQueryDenomHash(),
GetCmdQueryTotalEscrowForDenom(),
)

return queryCmd
Expand Down
33 changes: 33 additions & 0 deletions modules/apps/transfer/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,36 @@ func GetCmdQueryDenomHash() *cobra.Command {
flags.AddQueryFlagsToCmd(cmd)
return cmd
}

// GetCmdQueryTotalEscrowForDenom defines the command to query the total amount of tokens in escrow for a denom
func GetCmdQueryTotalEscrowForDenom() *cobra.Command {
cmd := &cobra.Command{
Use: "token-escrow [denom]",
Short: "Query the total amount of tokens in escrow for a denom",
Long: "Query the total amount of tokens in escrow for a denom",
Example: fmt.Sprintf("%s query ibc-transfer token-escrow uosmo", version.AppName),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)

req := &types.QueryTotalEscrowForDenomRequest{
Denom: args[0],
}

res, err := queryClient.TotalEscrowForDenom(cmd.Context(), req)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)
return cmd
}
13 changes: 10 additions & 3 deletions modules/apps/transfer/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,20 @@ func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) {
}

k.SetParams(ctx, state.Params)

// Every denom will have only one total escrow amount, since any
// duplicate entry will fail validation in Validate of GenesisState
for _, denomEscrow := range state.TotalEscrowed {
k.SetTotalEscrowForDenom(ctx, denomEscrow.Denom, denomEscrow.Amount)
}
}

// ExportGenesis exports ibc-transfer module's portID and denom trace info into its genesis state.
func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
return &types.GenesisState{
PortId: k.GetPort(ctx),
DenomTraces: k.GetAllDenomTraces(ctx),
Params: k.GetParams(ctx),
PortId: k.GetPort(ctx),
DenomTraces: k.GetAllDenomTraces(ctx),
Params: k.GetParams(ctx),
TotalEscrowed: k.GetAllTotalEscrowed(ctx),
}
}
39 changes: 28 additions & 11 deletions modules/apps/transfer/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,52 @@ package keeper_test
import (
"fmt"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
)

func (suite *KeeperTestSuite) TestGenesis() {
var (
path string
traces types.Traces
)
getTrace := func(index uint) string {
return fmt.Sprintf("transfer/channelToChain%d", index)
}

for i := 0; i < 5; i++ {
prefix := fmt.Sprintf("transfer/channelToChain%d", i)
if i == 0 {
path = prefix
} else {
path = prefix + "/" + path
var (
traces types.Traces
escrows sdk.Coins
pathsAndEscrowAmounts = []struct {
path string
escrow string
}{
{getTrace(0), "10"},
{fmt.Sprintf("%s/%s", getTrace(1), getTrace(0)), "100000"},
{fmt.Sprintf("%s/%s/%s", getTrace(2), getTrace(1), getTrace(0)), "10000000000"},
{fmt.Sprintf("%s/%s/%s/%s", getTrace(3), getTrace(2), getTrace(1), getTrace(0)), "1000000000000000"},
{fmt.Sprintf("%s/%s/%s/%s/%s", getTrace(4), getTrace(3), getTrace(2), getTrace(1), getTrace(0)), "100000000000000000000"},
}
)

for _, pathAndEscrowMount := range pathsAndEscrowAmounts {
denomTrace := types.DenomTrace{
BaseDenom: "uatom",
Path: path,
Path: pathAndEscrowMount.path,
}
traces = append(types.Traces{denomTrace}, traces...)
suite.chainA.GetSimApp().TransferKeeper.SetDenomTrace(suite.chainA.GetContext(), denomTrace)

denom := denomTrace.IBCDenom()
amount, ok := math.NewIntFromString(pathAndEscrowMount.escrow)
suite.Require().True(ok)
escrows = append(sdk.NewCoins(sdk.NewCoin(denom, amount)), escrows...)
suite.chainA.GetSimApp().TransferKeeper.SetTotalEscrowForDenom(suite.chainA.GetContext(), denom, amount)
}

genesis := suite.chainA.GetSimApp().TransferKeeper.ExportGenesis(suite.chainA.GetContext())

suite.Require().Equal(types.PortID, genesis.PortId)
suite.Require().Equal(traces.Sort(), genesis.DenomTraces)
suite.Require().Equal(escrows.Sort(), genesis.TotalEscrowed)

suite.Require().NotPanics(func() {
suite.chainA.GetSimApp().TransferKeeper.InitGenesis(suite.chainA.GetContext(), *genesis)
Expand Down
19 changes: 19 additions & 0 deletions modules/apps/transfer/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,22 @@ func (k Keeper) EscrowAddress(c context.Context, req *types.QueryEscrowAddressRe
EscrowAddress: addr.String(),
}, nil
}

// TotalEscrowForDenom implements the TotalEscrowForDenom gRPC method.
func (k Keeper) TotalEscrowForDenom(c context.Context, req *types.QueryTotalEscrowForDenomRequest) (*types.QueryTotalEscrowForDenomResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

ctx := sdk.UnwrapSDKContext(c)

if err := sdk.ValidateDenom(req.Denom); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

denomAmount := k.GetTotalEscrowForDenom(ctx, req.Denom)

return &types.QueryTotalEscrowForDenomResponse{
Amount: denomAmount,
}, nil
}
Loading

0 comments on commit fa9418f

Please sign in to comment.