Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
## [Unreleased]

### Features

* [\#8573](https://github.com/cosmos/ibc-go/pull/8573) Support custom address codecs in transfer, PFM, and rate limiting.
* [\#8285](https://github.com/cosmos/ibc-go/pull/8285) Packet forward middleware.
* [\#8545](https://github.com/cosmos/ibc-go/pull/8545) Support sending multiple payloads in the same packet for atomic payload execution.
* [\#8473](https://github.com/cosmos/ibc-go/pull/8473) Support sending v2 packets on v1 channel identifiers using aliasing.
Expand Down
38 changes: 38 additions & 0 deletions docs/docs/05-migrations/14-v10-to-v11.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,41 @@ Diff examples are shown after the list of overall changes:
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
```

The transfer module, the packet forward middleware, and the rate limiting middleware support custom address codecs. This feature is primarily added to support Cosmos EVM for IBC transfers. In a standard Cosmos SDK app, they are wired as follows:

```diff
app.TransferKeeper = ibctransferkeeper.NewKeeper(
appCodec,
+ app.AccountKeeper.AddressCodec(),
runtime.NewKVStoreService(keys[ibctransfertypes.StoreKey]),
app.IBCKeeper.ChannelKeeper,
app.MsgServiceRouter(),
app.AccountKeeper, app.BankKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
```

```diff
app.RateLimitKeeper = ratelimitkeeper.NewKeeper(
appCodec,
+ app.AccountKeeper.AddressCodec(),
runtime.NewKVStoreService(keys[ratelimittypes.StoreKey]),
app.IBCKeeper.ChannelKeeper,
app.IBCKeeper.ClientKeeper,
app.BankKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String()
)
```

```diff
app.PFMKeeper = packetforwardkeeper.NewKeeper(
appCodec,
+ app.AccountKeeper.AddressCodec(),
runtime.NewKVStoreService(keys[packetforwardtypes.StoreKey]),
app.TransferKeeper,
app.IBCKeeper.ChannelKeeper,
app.BankKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String()
)
```
4 changes: 3 additions & 1 deletion modules/apps/callbacks/testing/simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,9 @@ func NewSimApp(
// Create Transfer Keeper
// NOTE: the Transfer Keeper's ICS4Wrapper can later be replaced.
app.TransferKeeper = ibctransferkeeper.NewKeeper(
appCodec, runtime.NewKVStoreService(keys[ibctransfertypes.StoreKey]),
appCodec,
app.AccountKeeper.AddressCodec(),
runtime.NewKVStoreService(keys[ibctransfertypes.StoreKey]),
app.IBCKeeper.ChannelKeeper,
app.MsgServiceRouter(),
app.AccountKeeper, app.BankKeeper,
Expand Down
12 changes: 8 additions & 4 deletions modules/apps/packet-forward-middleware/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/hashicorp/go-metrics"

"cosmossdk.io/core/address"
corestore "cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
Expand Down Expand Up @@ -39,6 +40,7 @@ var (
type Keeper struct {
storeService corestore.KVStoreService
cdc codec.BinaryCodec
addressCodec address.Codec

transferKeeper types.TransferKeeper
channelKeeper types.ChannelKeeper
Expand All @@ -51,9 +53,11 @@ type Keeper struct {
}

// NewKeeper creates a new forward Keeper instance
func NewKeeper(cdc codec.BinaryCodec, storeService corestore.KVStoreService, transferKeeper types.TransferKeeper, channelKeeper types.ChannelKeeper, bankKeeper types.BankKeeper, authority string) *Keeper {
func NewKeeper(cdc codec.BinaryCodec, addressCodec address.Codec, storeService corestore.KVStoreService, transferKeeper types.TransferKeeper, channelKeeper types.ChannelKeeper, bankKeeper types.BankKeeper, authority string,
) *Keeper {
return &Keeper{
cdc: cdc,
addressCodec: addressCodec,
storeService: storeService,
transferKeeper: transferKeeper,
// Defaults to using the channel keeper as the ICS4Wrapper
Expand Down Expand Up @@ -102,7 +106,7 @@ func (k *Keeper) moveFundsToUserRecoverableAccount(ctx sdk.Context, packet chann
denom := token.GetDenom()
coin := sdk.NewCoin(denom.IBCDenom(), amount)

userAccount, err := userRecoverableAccount(inFlightPacket)
userAccount, err := k.userRecoverableAccount(inFlightPacket)
if err != nil {
return fmt.Errorf("failed to get user recoverable account: %w", err)
}
Expand Down Expand Up @@ -135,11 +139,11 @@ func (k *Keeper) moveFundsToUserRecoverableAccount(ctx sdk.Context, packet chann
// If the destination receiver of the original packet is a valid bech32 address for this chain, we use that address.
// Otherwise, if the sender of the original packet is a valid bech32 address for another chain, we translate that address to this chain.
// Note that for the fallback, the coin type of the source chain sender account must be compatible with this chain.
func userRecoverableAccount(inFlightPacket *types.InFlightPacket) (sdk.AccAddress, error) {
func (k *Keeper) userRecoverableAccount(inFlightPacket *types.InFlightPacket) (sdk.AccAddress, error) {
var originalData transfertypes.FungibleTokenPacketData
err := transfertypes.ModuleCdc.UnmarshalJSON(inFlightPacket.PacketData, &originalData)
if err == nil { // if NO error
sender, err := sdk.AccAddressFromBech32(originalData.Receiver)
sender, err := k.addressCodec.StringToBytes(originalData.Receiver)
if err == nil { // if NO error
return sender, nil
}
Expand Down
124 changes: 78 additions & 46 deletions modules/apps/packet-forward-middleware/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper_test
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"testing"
"time"
Expand All @@ -21,6 +22,7 @@ import (
clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types"
ibctesting "github.com/cosmos/ibc-go/v10/testing"
ibcmock "github.com/cosmos/ibc-go/v10/testing/mock"
)

type KeeperTestSuite struct {
Expand Down Expand Up @@ -161,70 +163,100 @@ func (s *KeeperTestSuite) TestWriteAcknowledgementForForwardedPacket() {
}

func (s *KeeperTestSuite) TestForwardTransferPacket() {
s.SetupTest()
path := ibctesting.NewTransferPath(s.chainA, s.chainB)
path.Setup()

pfmKeeper := keeper.NewKeeper(s.chainA.GetSimApp().AppCodec(), runtime.NewKVStoreService(s.chainA.GetSimApp().GetKey(pfmtypes.StoreKey)), &transferMock{}, s.chainA.GetSimApp().IBCKeeper.ChannelKeeper, s.chainA.GetSimApp().BankKeeper, "authority")
var (
pfmKeeper *keeper.Keeper
initialSender string
finalReceiver string
)
tests := []struct {
name string
malleate func()
}{
{
name: "success: standard cosmos address",
malleate: func() {},
},
{
name: "success: with hex address codec",
malleate: func() {
pfmKeeper = keeper.NewKeeper(s.chainA.GetSimApp().AppCodec(), ibcmock.TestAddressCodec{}, runtime.NewKVStoreService(s.chainA.GetSimApp().GetKey(pfmtypes.StoreKey)), &transferMock{}, s.chainA.GetSimApp().IBCKeeper.ChannelKeeper, s.chainA.GetSimApp().BankKeeper, "authority")

ctx := s.chainA.GetContext()
srcPacket := channeltypes.Packet{
Data: []byte{1},
Sequence: 1,
SourcePort: path.EndpointA.ChannelConfig.PortID,
SourceChannel: path.EndpointA.ChannelID,
DestinationPort: path.EndpointB.ChannelConfig.PortID,
DestinationChannel: path.EndpointB.ChannelID,
TimeoutHeight: clienttypes.Height{
RevisionNumber: 10,
RevisionHeight: 100,
initialSender = hex.EncodeToString(s.chainA.SenderAccount.GetAddress().Bytes())
finalReceiver = hex.EncodeToString(s.chainB.SenderAccount.GetAddress().Bytes())
},
},
TimeoutTimestamp: 10101001,
}

retries := uint8(2)
timeout := time.Duration(1010101010)
nonRefundable := false
for _, tc := range tests {
s.Run(tc.name, func() {
s.SetupTest()
path := ibctesting.NewTransferPath(s.chainA, s.chainB)
path.Setup()

metadata := pfmtypes.ForwardMetadata{
Receiver: "first-receiver",
Port: path.EndpointA.ChannelConfig.PortID,
Channel: path.EndpointA.ChannelID,
Timeout: timeout,
Retries: &retries,
Next: nil,
}
pfmKeeper = keeper.NewKeeper(s.chainA.GetSimApp().AppCodec(), s.chainA.GetSimApp().AccountKeeper.AddressCodec(), runtime.NewKVStoreService(s.chainA.GetSimApp().GetKey(pfmtypes.StoreKey)), &transferMock{}, s.chainA.GetSimApp().IBCKeeper.ChannelKeeper, s.chainA.GetSimApp().BankKeeper, "authority")

initialSender := s.chainA.SenderAccount.GetAddress()
finalReceiver := s.chainB.SenderAccount.GetAddress()
ctx := s.chainA.GetContext()
srcPacket := channeltypes.Packet{
Data: []byte{1},
Sequence: 1,
SourcePort: path.EndpointA.ChannelConfig.PortID,
SourceChannel: path.EndpointA.ChannelID,
DestinationPort: path.EndpointB.ChannelConfig.PortID,
DestinationChannel: path.EndpointB.ChannelID,
TimeoutHeight: clienttypes.Height{
RevisionNumber: 10,
RevisionHeight: 100,
},
TimeoutTimestamp: 10101001,
}

err := pfmKeeper.ForwardTransferPacket(ctx, nil, srcPacket, initialSender.String(), finalReceiver.String(), metadata, sdk.NewInt64Coin("denom", 1000), 2, timeout, nil, nonRefundable)
s.Require().NoError(err)
retries := uint8(2)
timeout := time.Duration(1010101010)
nonRefundable := false

// Get the inflight packer
inflightPacket, err := pfmKeeper.GetInflightPacket(ctx, srcPacket)
s.Require().NoError(err)
metadata := pfmtypes.ForwardMetadata{
Receiver: "first-receiver",
Port: path.EndpointA.ChannelConfig.PortID,
Channel: path.EndpointA.ChannelID,
Timeout: timeout,
Retries: &retries,
Next: nil,
}

s.Require().Equal(inflightPacket.RetriesRemaining, int32(retries))
initialSender = s.chainA.SenderAccount.GetAddress().String()
finalReceiver = s.chainB.SenderAccount.GetAddress().String()

// Call the same function again with inflight packet. Num retries should decrease.
err = pfmKeeper.ForwardTransferPacket(ctx, inflightPacket, srcPacket, initialSender.String(), finalReceiver.String(), metadata, sdk.NewInt64Coin("denom", 1000), 2, timeout, nil, nonRefundable)
s.Require().NoError(err)
tc.malleate()

// Get the inflight packer
inflightPacket2, err := pfmKeeper.GetInflightPacket(ctx, srcPacket)
s.Require().NoError(err)
err := pfmKeeper.ForwardTransferPacket(ctx, nil, srcPacket, initialSender, finalReceiver, metadata, sdk.NewInt64Coin("denom", 1000), 2, timeout, nil, nonRefundable)
s.Require().NoError(err)

s.Require().Equal(inflightPacket.RetriesRemaining, inflightPacket2.RetriesRemaining)
s.Require().Equal(int32(retries-1), inflightPacket.RetriesRemaining)
// Get the inflight packer
inflightPacket, err := pfmKeeper.GetInflightPacket(ctx, srcPacket)
s.Require().NoError(err)

s.Require().Equal(inflightPacket.RetriesRemaining, int32(retries))

// Call the same function again with inflight packet. Num retries should decrease.
err = pfmKeeper.ForwardTransferPacket(ctx, inflightPacket, srcPacket, initialSender, finalReceiver, metadata, sdk.NewInt64Coin("denom", 1000), 2, timeout, nil, nonRefundable)
s.Require().NoError(err)

// Get the inflight packer
inflightPacket2, err := pfmKeeper.GetInflightPacket(ctx, srcPacket)
s.Require().NoError(err)

s.Require().Equal(inflightPacket.RetriesRemaining, inflightPacket2.RetriesRemaining)
s.Require().Equal(int32(retries-1), inflightPacket.RetriesRemaining)
})
}
}

func (s *KeeperTestSuite) TestForwardTransferPacketWithNext() {
s.SetupTest()
path := ibctesting.NewTransferPath(s.chainA, s.chainB)
path.Setup()

pfmKeeper := keeper.NewKeeper(s.chainA.GetSimApp().AppCodec(), runtime.NewKVStoreService(s.chainA.GetSimApp().GetKey(pfmtypes.StoreKey)), &transferMock{}, s.chainA.GetSimApp().IBCKeeper.ChannelKeeper, s.chainA.GetSimApp().BankKeeper, "authority")
pfmKeeper := keeper.NewKeeper(s.chainA.GetSimApp().AppCodec(), s.chainA.GetSimApp().AccountKeeper.AddressCodec(), runtime.NewKVStoreService(s.chainA.GetSimApp().GetKey(pfmtypes.StoreKey)), &transferMock{}, s.chainA.GetSimApp().IBCKeeper.ChannelKeeper, s.chainA.GetSimApp().BankKeeper, "authority")
ctx := s.chainA.GetContext()
srcPacket := channeltypes.Packet{
Data: []byte{1},
Expand Down Expand Up @@ -283,7 +315,7 @@ func (s *KeeperTestSuite) TestRetryTimeoutErrorGettingNext() {
path := ibctesting.NewTransferPath(s.chainA, s.chainB)
path.Setup()

pfmKeeper := keeper.NewKeeper(s.chainA.GetSimApp().AppCodec(), runtime.NewKVStoreService(s.chainA.GetSimApp().GetKey(pfmtypes.StoreKey)), &transferMock{}, s.chainA.GetSimApp().IBCKeeper.ChannelKeeper, s.chainA.GetSimApp().BankKeeper, "authority")
pfmKeeper := keeper.NewKeeper(s.chainA.GetSimApp().AppCodec(), s.chainA.GetSimApp().AccountKeeper.AddressCodec(), runtime.NewKVStoreService(s.chainA.GetSimApp().GetKey(pfmtypes.StoreKey)), &transferMock{}, s.chainA.GetSimApp().IBCKeeper.ChannelKeeper, s.chainA.GetSimApp().BankKeeper, "authority")
ctx := s.chainA.GetContext()

// Create a transfer detail with invalid memo that will cause GetPacketMetadataFromPacketdata to fail
Expand Down
5 changes: 4 additions & 1 deletion modules/apps/rate-limiting/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"strings"

"cosmossdk.io/core/address"
corestore "cosmossdk.io/core/store"
"cosmossdk.io/log"

Expand All @@ -19,6 +20,7 @@ import (
type Keeper struct {
storeService corestore.KVStoreService
cdc codec.BinaryCodec
addressCodec address.Codec

ics4Wrapper porttypes.ICS4Wrapper
channelKeeper types.ChannelKeeper
Expand All @@ -29,13 +31,14 @@ type Keeper struct {
}

// NewKeeper creates a new rate-limiting Keeper instance
func NewKeeper(cdc codec.BinaryCodec, storeService corestore.KVStoreService, channelKeeper types.ChannelKeeper, clientKeeper types.ClientKeeper, bankKeeper types.BankKeeper, authority string) *Keeper {
func NewKeeper(cdc codec.BinaryCodec, addressCodec address.Codec, storeService corestore.KVStoreService, channelKeeper types.ChannelKeeper, clientKeeper types.ClientKeeper, bankKeeper types.BankKeeper, authority string) *Keeper {
if strings.TrimSpace(authority) == "" {
panic(errors.New("authority must be non-empty"))
}

return &Keeper{
cdc: cdc,
addressCodec: addressCodec,
storeService: storeService,
// Defaults to using the channel keeper as the ICS4Wrapper
// This can be overridden later with WithICS4Wrapper (e.g. by the middleware stack wiring)
Expand Down
18 changes: 18 additions & 0 deletions modules/apps/rate-limiting/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/cosmos/ibc-go/v10/modules/apps/rate-limiting/keeper"
ratelimittypes "github.com/cosmos/ibc-go/v10/modules/apps/rate-limiting/types"
ibctesting "github.com/cosmos/ibc-go/v10/testing"
ibcmock "github.com/cosmos/ibc-go/v10/testing/mock"
)

type KeeperTestSuite struct {
Expand Down Expand Up @@ -44,6 +45,22 @@ func (s *KeeperTestSuite) TestNewKeeper() {
instantiateFn: func() {
keeper.NewKeeper(
s.chainA.GetSimApp().AppCodec(),
s.chainA.GetSimApp().AccountKeeper.AddressCodec(),
runtime.NewKVStoreService(s.chainA.GetSimApp().GetKey(ratelimittypes.StoreKey)),
s.chainA.GetSimApp().IBCKeeper.ChannelKeeper,
s.chainA.GetSimApp().IBCKeeper.ClientKeeper, // Add clientKeeper
s.chainA.GetSimApp().BankKeeper,
s.chainA.GetSimApp().ICAHostKeeper.GetAuthority(),
)
},
panicMsg: "",
},
{
name: "success: custom address codec",
instantiateFn: func() {
keeper.NewKeeper(
s.chainA.GetSimApp().AppCodec(),
ibcmock.TestAddressCodec{},
runtime.NewKVStoreService(s.chainA.GetSimApp().GetKey(ratelimittypes.StoreKey)),
s.chainA.GetSimApp().IBCKeeper.ChannelKeeper,
s.chainA.GetSimApp().IBCKeeper.ClientKeeper, // Add clientKeeper
Expand All @@ -58,6 +75,7 @@ func (s *KeeperTestSuite) TestNewKeeper() {
instantiateFn: func() {
keeper.NewKeeper(
s.chainA.GetSimApp().AppCodec(),
s.chainA.GetSimApp().AccountKeeper.AddressCodec(),
runtime.NewKVStoreService(s.chainA.GetSimApp().GetKey(ratelimittypes.StoreKey)),
s.chainA.GetSimApp().IBCKeeper.ChannelKeeper,
s.chainA.GetSimApp().IBCKeeper.ClientKeeper, // clientKeeper
Expand Down
Loading
Loading