Skip to content

feat: added ics27-2 gmp application #8352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 41 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2b8ebde
feat: added proto types
srdtrk Apr 25, 2025
e64d4b8
chore: ran proto-all
srdtrk May 2, 2025
33760e8
Merge branch 'main' into serdar/xxx-contract-calls
srdtrk May 2, 2025
7f98428
chore: generate proto
srdtrk May 2, 2025
2b29b85
imp: added type helpers
srdtrk May 2, 2025
5b198c7
imp: added new proto
srdtrk May 2, 2025
c7289d5
imp: added query.proto
srdtrk May 2, 2025
add062d
Merge branch 'main' into serdar/xxx-contract-calls
srdtrk May 2, 2025
83be763
chore: gen proto types
srdtrk May 2, 2025
0394e66
imp: regen proto
srdtrk May 2, 2025
0f5dbfb
imp: save progress
srdtrk May 2, 2025
4fed4b9
feat: implemented basic module with autocli
srdtrk May 4, 2025
2d21a1e
imp: added ibc boilerplate
srdtrk May 4, 2025
7dfb3e6
feat: add module to simapp
srdtrk May 5, 2025
5cd5bce
imp: added account keeper
srdtrk May 5, 2025
d0a7742
imp: remoce ics4wrapper
srdtrk May 5, 2025
918f808
imp: implemented first message server
srdtrk May 6, 2025
23366e8
imp: verify autocli
srdtrk May 6, 2025
230ef6a
imp: implemented OnSendPacket
srdtrk May 6, 2025
032dd63
imp: implemented ack and timeout entry points
srdtrk May 6, 2025
707fa58
imp: more boilerplate
srdtrk May 6, 2025
06495a4
imp: added ack to proto
srdtrk May 6, 2025
eceb5b1
imp: added ack helpers
srdtrk May 6, 2025
2bd764d
imp: more boilerplate
srdtrk May 6, 2025
d3a9cd9
feat: added account gen
srdtrk May 7, 2025
0fab11f
imp: added cosmos tx
srdtrk May 7, 2025
134b82a
imp: implemented receive
srdtrk May 7, 2025
ef9559a
imp: implemented query
srdtrk May 7, 2025
fa61151
lint
srdtrk May 7, 2025
0926c7d
imp: added gmp to wasm simapp
srdtrk May 7, 2025
c6203a9
style: lint
srdtrk May 7, 2025
18c8157
style: lint
srdtrk May 7, 2025
be8367e
chore: bump go version
srdtrk May 7, 2025
7df1aab
Merge branch 'main' into serdar/xxx-contract-calls
srdtrk May 8, 2025
ee75f8b
imp: attempt to improve module wiring
srdtrk May 8, 2025
e6c856e
imp: fix attempt
srdtrk May 8, 2025
d84336d
imp: removed gmp from unused simapp
srdtrk May 8, 2025
cc9968d
fix: keeper wiring
srdtrk May 8, 2025
a841ca9
imp: allow receiver to be empty
srdtrk May 9, 2025
62159d4
imp: added logger
srdtrk May 9, 2025
6151f8f
imp: no err if acc already exists
srdtrk May 9, 2025
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 go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ replace github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.2021

require (
cosmossdk.io/api v0.9.2
cosmossdk.io/collections v1.2.0
cosmossdk.io/core v0.11.3
cosmossdk.io/errors v1.0.2
cosmossdk.io/log v1.5.1
Expand Down Expand Up @@ -41,7 +42,6 @@ require (
cloud.google.com/go/iam v1.2.2 // indirect
cloud.google.com/go/monitoring v1.21.2 // indirect
cloud.google.com/go/storage v1.49.0 // indirect
cosmossdk.io/collections v1.2.0 // indirect
cosmossdk.io/depinject v1.2.0 // indirect
cosmossdk.io/schema v1.1.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
Expand Down
130 changes: 130 additions & 0 deletions modules/apps/27-gmp/ibc_module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package gmp

import (
"fmt"

errorsmod "cosmossdk.io/errors"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/cosmos/ibc-go/v10/modules/apps/27-gmp/keeper"
"github.com/cosmos/ibc-go/v10/modules/apps/27-gmp/types"
clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types"
channeltypesv2 "github.com/cosmos/ibc-go/v10/modules/core/04-channel/v2/types"
"github.com/cosmos/ibc-go/v10/modules/core/api"
ibcerrors "github.com/cosmos/ibc-go/v10/modules/core/errors"
)

var _ api.IBCModule = (*IBCModule)(nil)

// IBCModule implements the ICS26 interface for transfer given the transfer keeper.
type IBCModule struct {
keeper keeper.Keeper
}

// NewIBCModule creates a new IBCModule given the keeper
func NewIBCModule(k keeper.Keeper) *IBCModule {
return &IBCModule{
keeper: k,
}
}

func (*IBCModule) OnSendPacket(ctx sdk.Context, sourceChannel string, destinationChannel string, sequence uint64, payload channeltypesv2.Payload, signer sdk.AccAddress) error {
if payload.SourcePort != types.PortID || payload.DestinationPort != types.PortID {
return errorsmod.Wrapf(channeltypesv2.ErrInvalidPacket, "payload port ID is invalid: expected %s, got sourcePort: %s destPort: %s", types.PortID, payload.SourcePort, payload.DestinationPort)
}
if !clienttypes.IsValidClientID(sourceChannel) || !clienttypes.IsValidClientID(destinationChannel) {
return errorsmod.Wrapf(channeltypesv2.ErrInvalidPacket, "client IDs must be in valid format: {string}-{number}")
}

data, err := types.UnmarshalPacketData(payload.Value, payload.Version, payload.Encoding)
if err != nil {
return err
}

if err := data.ValidateBasic(); err != nil {
return errorsmod.Wrapf(err, "failed to validate %s packet data", types.Version)
}

sender, err := sdk.AccAddressFromBech32(data.Sender)
if err != nil {
return err
}

if !signer.Equals(sender) {
return errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "sender %s is different from signer %s", sender, signer)
}

// TODO: emit event and telemetry

return nil
}

func (im *IBCModule) OnRecvPacket(ctx sdk.Context, sourceChannel, destinationChannel string, sequence uint64, payload channeltypesv2.Payload, relayer sdk.AccAddress) channeltypesv2.RecvPacketResult {
if payload.SourcePort != types.PortID || payload.DestinationPort != types.PortID {
return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Failure,
}
}
if payload.Version != types.Version {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check exist in SendPacket

return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Failure,
}
}

packetData, ackErr := types.UnmarshalPacketData(payload.Value, payload.Version, payload.Encoding)
if ackErr != nil {
im.keeper.Logger(ctx).Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), sequence))
return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Failure,
}
}

if ackErr := packetData.ValidateBasic(); ackErr != nil {
im.keeper.Logger(ctx).Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), sequence))
return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Failure,
}
}

result, ackErr := im.keeper.OnRecvPacket(
ctx,
packetData,
payload.SourcePort,
sourceChannel,
payload.DestinationPort,
destinationChannel,
)
if ackErr != nil {
im.keeper.Logger(ctx).Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), sequence))
return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Failure,
}
}

ack := types.NewAcknowledgement(result)
ackBz, ackErr := types.MarshalAcknowledgement(&ack, types.Version, payload.Encoding)
if ackErr != nil {
im.keeper.Logger(ctx).Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), sequence))
return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Failure,
}
}

im.keeper.Logger(ctx).Info("successfully handled ICS-27 GMP packet", "sequence", sequence)

// TODO: implement telemetry

return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Success,
Acknowledgement: ackBz,
}
}

func (*IBCModule) OnTimeoutPacket(_ sdk.Context, _, _ string, _ uint64, _ channeltypesv2.Payload, _ sdk.AccAddress) error {
return nil
}

func (*IBCModule) OnAcknowledgementPacket(_ sdk.Context, _, _ string, _ uint64, _ []byte, _ channeltypesv2.Payload, _ sdk.AccAddress) error {
return nil
}
66 changes: 66 additions & 0 deletions modules/apps/27-gmp/keeper/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package keeper

import (
"context"

"cosmossdk.io/collections"
errorsmod "cosmossdk.io/errors"

"github.com/cosmos/ibc-go/v10/modules/apps/27-gmp/types"
)

// getOrCreateICS27Account retrieves an existing ICS27 account or creates a new one if it doesn't exist.
func (k Keeper) getOrCreateICS27Account(ctx context.Context, accountID *types.AccountIdentifier) (*types.ICS27Account, error) {
existingIcs27Account, err := k.Accounts.Get(ctx, collections.Join3(accountID.ClientId, accountID.Sender, accountID.Salt))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you have this additional storage out of state?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems redundant with accountKeeper

if err == nil {
return &existingIcs27Account, nil
} else if !errorsmod.IsOf(err, collections.ErrNotFound) {
return nil, err
}

// Create a new account
newAddr, err := types.BuildAddressPredictable(accountID)
if err != nil {
return nil, err
}

existingAcc := k.accountKeeper.GetAccount(ctx, newAddr)
if existingAcc != nil {
// TODO: ensure this cannot be abused (refactor after this is resolved)
ics27Account := types.NewICS27Account(existingAcc.GetAddress().String(), accountID)
if err := k.Accounts.Set(ctx, collections.Join3(accountID.ClientId, accountID.Sender, accountID.Salt), ics27Account); err != nil {
return nil, errorsmod.Wrapf(err, "failed to set account %s in store", ics27Account)
}

return &ics27Account, nil
}

newAcc := k.accountKeeper.NewAccountWithAddress(ctx, newAddr)
k.accountKeeper.SetAccount(ctx, newAcc)

ics27Account := types.NewICS27Account(newAcc.GetAddress().String(), accountID)
if err := k.Accounts.Set(ctx, collections.Join3(accountID.ClientId, accountID.Sender, accountID.Salt), ics27Account); err != nil {
return nil, errorsmod.Wrapf(err, "failed to set account %s in store", ics27Account)
}

k.Logger(ctx).Info("Created new ICS27 account", "account", ics27Account)
return &ics27Account, nil
}

// getOrComputeICS27Adderss retrieves an existing ICS27 account address or computes it if it doesn't exist. This doesn't modify the store.
func (k Keeper) getOrComputeICS27Address(ctx context.Context, accountID *types.AccountIdentifier) (string, error) {
existingIcs27Account, err := k.Accounts.Get(ctx, collections.Join3(accountID.ClientId, accountID.Sender, accountID.Salt))
if err == nil {
return existingIcs27Account.Address, nil
} else if !errorsmod.IsOf(err, collections.ErrNotFound) {
return "", err
}

// Compute a new address
newAddr, err := types.BuildAddressPredictable(accountID)
if err != nil {
return "", err
}

return newAddr.String(), nil
}
53 changes: 53 additions & 0 deletions modules/apps/27-gmp/keeper/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package keeper

import (
"context"

"cosmossdk.io/collections"
errorsmod "cosmossdk.io/errors"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/cosmos/ibc-go/v10/modules/apps/27-gmp/types"
ibcerrors "github.com/cosmos/ibc-go/v10/modules/core/errors"
)

// InitGenesis initializes the module state from a genesis state.
func (k *Keeper) InitGenesis(ctx context.Context, data *types.GenesisState) error {
for _, account := range data.Ics27Accounts {
if _, err := sdk.AccAddressFromBech32(account.AccountAddress); err != nil {
return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err)
}
if _, err := sdk.AccAddressFromBech32(account.AccountId.Sender); err != nil {
return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err)
}

if err := k.Accounts.Set(ctx, collections.Join3(account.AccountId.ClientId, account.AccountId.Sender, account.AccountId.Salt), types.ICS27Account{
Address: account.AccountAddress,
AccountId: &account.AccountId,
}); err != nil {
return err
}
}

return nil
}

// ExportGenesis exports the module state to a genesis state.
func (k *Keeper) ExportGenesis(ctx context.Context) (*types.GenesisState, error) {
var accounts []types.RegisteredICS27Account
if err := k.Accounts.Walk(ctx, nil, func(key collections.Triple[string, string, []byte], value types.ICS27Account) (bool, error) {
accounts = append(accounts, types.RegisteredICS27Account{
AccountAddress: value.Address,
AccountId: *value.AccountId,
})

return false, nil
}); err != nil {
return nil, err
}

return &types.GenesisState{
Ics27Accounts: accounts,
}, nil
}
74 changes: 74 additions & 0 deletions modules/apps/27-gmp/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package keeper

import (
"context"
"errors"
"strings"

"cosmossdk.io/collections"
storetypes "cosmossdk.io/core/store"
"cosmossdk.io/log"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/cosmos/ibc-go/v10/modules/apps/27-gmp/types"
"github.com/cosmos/ibc-go/v10/modules/core/exported"
)

// Keeper defines the IBC fungible transfer keeper
type Keeper struct {
cdc codec.Codec

msgRouter types.MessageRouter

accountKeeper types.AccountKeeper

// the address capable of executing a MsgUpdateParams message. Typically, this
// should be the x/gov module account.
authority string

// state management
Schema collections.Schema
// Accounts is a map of (ClientID, Sender, Salt) to ICS27Account
Accounts collections.Map[collections.Triple[string, string, []byte], types.ICS27Account]
}

// NewKeeper creates a new Keeper instance
func NewKeeper(
cdc codec.Codec, storeService storetypes.KVStoreService,
accountKeeper types.AccountKeeper, msgRouter types.MessageRouter,
authority string,
) Keeper {
if strings.TrimSpace(authority) == "" {
panic(errors.New("authority must be non-empty"))
}

sb := collections.NewSchemaBuilder(storeService)
k := Keeper{
cdc: cdc,
msgRouter: msgRouter,
accountKeeper: accountKeeper,
authority: authority,
Accounts: collections.NewMap(sb, types.AccountsKey, "accounts", collections.TripleKeyCodec(collections.StringKey, collections.StringKey, collections.BytesKey), codec.CollValue[types.ICS27Account](cdc)),
}

schema, err := sb.Build()
if err != nil {
panic(err)
}

k.Schema = schema

return k
}

// GetAuthority returns the module's authority.
func (k Keeper) GetAuthority() string {
return k.authority
}

// Logger returns a module-specific logger.
func (Keeper) Logger(goCtx context.Context) log.Logger {
return sdk.UnwrapSDKContext(goCtx).Logger().With("module", "x/"+exported.ModuleName+"-"+types.ModuleName)
}
Loading
Loading