Skip to content
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

feat: channel-v2 genesis #7933

Merged
merged 8 commits into from
Feb 10, 2025
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
57 changes: 57 additions & 0 deletions modules/core/04-channel/v2/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package channelv2

import (
"context"

"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/keeper"
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
)

func InitGenesis(ctx context.Context, k *keeper.Keeper, gs types.GenesisState) {
// set acks
for _, ack := range gs.Acknowledgements {
k.SetPacketAcknowledgement(ctx, ack.ClientId, ack.Sequence, ack.Data)
}

// set commits
for _, commitment := range gs.Commitments {
k.SetPacketCommitment(ctx, commitment.ClientId, commitment.Sequence, commitment.Data)
}

// set receipts
for _, receipt := range gs.Receipts {
k.SetPacketReceipt(ctx, receipt.ClientId, receipt.Sequence)
}

// set send sequences
for _, seq := range gs.SendSequences {
k.SetNextSequenceSend(ctx, seq.ClientId, seq.Sequence)
}
}

func ExportGenesis(ctx context.Context, k *keeper.Keeper) types.GenesisState {
clientStates := k.ClientKeeper.GetAllGenesisClients(ctx)
gs := types.GenesisState{
Acknowledgements: make([]types.PacketState, 0),
Commitments: make([]types.PacketState, 0),
Receipts: make([]types.PacketState, 0),
SendSequences: make([]types.PacketSequence, 0),
}
for _, clientState := range clientStates {
acks := k.GetAllPacketAcknowledgementsForClient(ctx, clientState.ClientId)
gs.Acknowledgements = append(gs.Acknowledgements, acks...)

comms := k.GetAllPacketCommitmentsForClient(ctx, clientState.ClientId)
gs.Commitments = append(gs.Commitments, comms...)

receipts := k.GetAllPacketReceiptsForClient(ctx, clientState.ClientId)
gs.Receipts = append(gs.Receipts, receipts...)

seq, ok := k.GetNextSequenceSend(ctx, clientState.ClientId)
if ok {
gs.SendSequences = append(gs.SendSequences, types.NewPacketSequence(clientState.ClientId, seq))
}
}

return gs
}
61 changes: 61 additions & 0 deletions modules/core/04-channel/v2/genesis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package channelv2_test

import (
channelv2 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2"
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
ibctesting "github.com/cosmos/ibc-go/v9/testing"
)

// TestInitExportGenesis tests the import and export flow for the channel v2 keeper.
func (suite *ModuleTestSuite) TestInitExportGenesis() {
path := ibctesting.NewPath(suite.chainA, suite.chainB)
path.SetupV2()

path2 := ibctesting.NewPath(suite.chainA, suite.chainC)
path2.SetupV2()

app := suite.chainA.App

emptyGenesis := types.DefaultGenesisState()

// create a valid genesis state that uses the client keepers existing client IDs
clientStates := app.GetIBCKeeper().ClientKeeper.GetAllGenesisClients(suite.chainA.GetContext())
validGs := types.DefaultGenesisState()
for i, clientState := range clientStates {
ack := types.NewPacketState(clientState.ClientId, uint64(i+1), []byte("ack"))
receipt := types.NewPacketState(clientState.ClientId, uint64(i+1), []byte{byte(0x2)})
commitment := types.NewPacketState(clientState.ClientId, uint64(i+1), []byte("commit_hash"))
seq := types.NewPacketSequence(clientState.ClientId, uint64(i+1))

validGs.Acknowledgements = append(validGs.Acknowledgements, ack)
validGs.Receipts = append(validGs.Receipts, receipt)
validGs.Commitments = append(validGs.Commitments, commitment)
validGs.SendSequences = append(validGs.SendSequences, seq)
emptyGenesis.SendSequences = append(emptyGenesis.SendSequences, seq)
}

tests := []struct {
name string
genState types.GenesisState
}{
{
name: "no modifications genesis",
genState: emptyGenesis,
},
{
name: "valid",
genState: validGs,
},
}

for _, tt := range tests {
suite.Run(tt.name, func() {
channelV2Keeper := app.GetIBCKeeper().ChannelKeeperV2

channelv2.InitGenesis(suite.chainA.GetContext(), channelV2Keeper, tt.genState)

exported := channelv2.ExportGenesis(suite.chainA.GetContext(), channelV2Keeper)
suite.Require().Equal(tt.genState, exported)
})
}
}
58 changes: 58 additions & 0 deletions modules/core/04-channel/v2/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package keeper

import (
"bytes"
"context"

corestore "cosmossdk.io/core/store"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"

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

connectionkeeper "github.com/cosmos/ibc-go/v9/modules/core/03-connection/keeper"
Expand Down Expand Up @@ -193,3 +196,58 @@ func (k *Keeper) DeleteAsyncPacket(ctx context.Context, clientID string, sequenc
panic(err)
}
}

// extractSequenceFromKey takes the full store key as well as a packet store prefix and extracts
// the encoded sequence number from the key.
//
// This function panics of the provided key once trimmed is larger than 8 bytes as the expected
// sequence byte length is always 8.
func extractSequenceFromKey(key, storePrefix []byte) uint64 {
sequenceBz := bytes.TrimPrefix(key, storePrefix)
if len(sequenceBz) > 8 {
panic("sequence is too long - expected 8 bytes")
}
return sdk.BigEndianToUint64(sequenceBz)
}

// GetAllPacketCommitmentsForClient returns all stored PacketCommitments objects for a specified
// client ID.
func (k *Keeper) GetAllPacketCommitmentsForClient(ctx context.Context, clientID string) []types.PacketState {
return k.getAllPacketsForClientStore(ctx, clientID, hostv2.PacketCommitmentPrefixKey)
}

// GetAllPacketAcknowledgementsForClient returns all stored PacketAcknowledgements objects for a specified
// client ID.
func (k *Keeper) GetAllPacketAcknowledgementsForClient(ctx context.Context, clientID string) []types.PacketState {
return k.getAllPacketsForClientStore(ctx, clientID, hostv2.PacketAcknowledgementPrefixKey)
}

// GetAllPacketReceiptsForClient returns all stored PacketReceipts objects for a specified
// client ID.
func (k *Keeper) GetAllPacketReceiptsForClient(ctx context.Context, clientID string) []types.PacketState {
return k.getAllPacketsForClientStore(ctx, clientID, hostv2.PacketReceiptPrefixKey)
}

// prefixKeyConstructor is a function that constructs a store key for a specific packet store using the provided
// clientID.
type prefixKeyConstructor func(clientID string) []byte

// getAllPacketsForClientStore gets all PacketState objects for the specified clientID using a provided
// function for constructing the key prefix for the store.
//
// For example, to get all PacketReceipts for a clientID the hostv2.PacketReceiptPrefixKey function can be
// passed to get the PacketReceipt store key prefix.
func (k *Keeper) getAllPacketsForClientStore(ctx context.Context, clientID string, prefixFn prefixKeyConstructor) []types.PacketState {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
storePrefix := prefixFn(clientID)
iterator := storetypes.KVStorePrefixIterator(store, storePrefix)

var packets []types.PacketState
for ; iterator.Valid(); iterator.Next() {
sequence := extractSequenceFromKey(iterator.Key(), storePrefix)
state := types.NewPacketState(clientID, sequence, iterator.Value())

packets = append(packets, state)
}
return packets
}
3 changes: 1 addition & 2 deletions modules/core/04-channel/v2/module.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package client
package channelv2

import (
"github.com/spf13/cobra"
Expand All @@ -7,7 +7,6 @@ import (
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
)

// Name returns the IBC channel/v2 name
func Name() string {
return types.SubModuleName
}
Expand Down
31 changes: 31 additions & 0 deletions modules/core/04-channel/v2/module_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package channelv2_test

import (
"testing"

testifysuite "github.com/stretchr/testify/suite"

ibctesting "github.com/cosmos/ibc-go/v9/testing"
)

func TestModuleTestSuite(t *testing.T) {
testifysuite.Run(t, new(ModuleTestSuite))
}

type ModuleTestSuite struct {
testifysuite.Suite

coordinator *ibctesting.Coordinator

// testing chains used for convenience and readability
chainA *ibctesting.TestChain
chainB *ibctesting.TestChain
chainC *ibctesting.TestChain
}

func (suite *ModuleTestSuite) SetupTest() {
suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3)
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1))
suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2))
suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(3))
}
2 changes: 2 additions & 0 deletions modules/core/04-channel/v2/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ type ClientKeeper interface {
GetClientConsensusState(ctx context.Context, clientID string, height exported.Height) (exported.ConsensusState, bool)
// GetClientCounterparty returns the counterpartyInfo given a clientID
GetClientCounterparty(ctx context.Context, clientID string) (clienttypes.CounterpartyInfo, bool)
// GetAllGenesisClients returns all the clients in state with their client ids returned as IdentifiedClientState
GetAllGenesisClients(ctx context.Context) clienttypes.IdentifiedClientStates
gjermundgaraba marked this conversation as resolved.
Show resolved Hide resolved
}
14 changes: 10 additions & 4 deletions modules/core/24-host/v2/packet_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

const (
PacketCommitmentBasePrefix = byte(1)
gjermundgaraba marked this conversation as resolved.
Show resolved Hide resolved
PacketReceiptBasePrefix = byte(2)
PacketAcknowledgementBasePrefix = byte(3)
)

// PacketCommitmentPrefixKey returns the store key prefix under which packet commitments for a particular channel are stored.
// channelID must be a generated identifier, not provided externally so key collisions are not possible.
func PacketCommitmentPrefixKey(channelID string) []byte {
return append([]byte(channelID), byte(1))
return append([]byte(channelID), PacketCommitmentBasePrefix)
}

// PacketCommitmentKey returns the store key of under which a packet commitment is stored.
// channelID must be a generated identifier, not provided externally so key collisions are not possible.
func PacketCommitmentKey(channelID string, sequence uint64) []byte {
return append(PacketCommitmentPrefixKey((channelID)), sdk.Uint64ToBigEndian(sequence)...)
return append(PacketCommitmentPrefixKey(channelID), sdk.Uint64ToBigEndian(sequence)...)
}

// PacketReceiptPrefixKey returns the store key prefix under which packet receipts for a particular channel are stored.
// channelID must be a generated identifier, not provided externally so key collisions are not possible.
func PacketReceiptPrefixKey(channelID string) []byte {
return append([]byte(channelID), byte(2))
return append([]byte(channelID), PacketReceiptBasePrefix)
}

// PacketReceiptKey returns the store key of under which a packet receipt is stored.
Expand All @@ -33,7 +39,7 @@ func PacketReceiptKey(channelID string, sequence uint64) []byte {
// PacketAcknowledgementPrefixKey returns the store key prefix under which packet acknowledgements for a particular channel are stored.
// channelID must be a generated identifier, not provided externally so key collisions are not possible.
func PacketAcknowledgementPrefixKey(channelID string) []byte {
return append([]byte(channelID), byte(3))
return append([]byte(channelID), PacketAcknowledgementBasePrefix)
}

// PacketAcknowledgementKey returns the store key of under which a packet acknowledgement is stored.
Expand Down
3 changes: 3 additions & 0 deletions modules/core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
client "github.com/cosmos/ibc-go/v9/modules/core/02-client"
connection "github.com/cosmos/ibc-go/v9/modules/core/03-connection"
channel "github.com/cosmos/ibc-go/v9/modules/core/04-channel"
channelv2 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2"
"github.com/cosmos/ibc-go/v9/modules/core/keeper"
"github.com/cosmos/ibc-go/v9/modules/core/types"
)
Expand All @@ -16,6 +17,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs *types.GenesisState) {
client.InitGenesis(ctx, k.ClientKeeper, gs.ClientGenesis)
connection.InitGenesis(ctx, k.ConnectionKeeper, gs.ConnectionGenesis)
channel.InitGenesis(ctx, k.ChannelKeeper, gs.ChannelGenesis)
channelv2.InitGenesis(ctx, k.ChannelKeeperV2, gs.ChannelV2Genesis)
}

// ExportGenesis returns the ibc exported genesis.
Expand All @@ -24,5 +26,6 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
ClientGenesis: client.ExportGenesis(ctx, k.ClientKeeper),
ConnectionGenesis: connection.ExportGenesis(ctx, k.ConnectionKeeper),
ChannelGenesis: channel.ExportGenesis(ctx, k.ChannelKeeper),
ChannelV2Genesis: channelv2.ExportGenesis(ctx, k.ChannelKeeperV2),
}
}
46 changes: 46 additions & 0 deletions modules/core/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types"
connectiontypes "github.com/cosmos/ibc-go/v9/modules/core/03-connection/types"
channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types"
channelv2types "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
commitmenttypes "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types"
"github.com/cosmos/ibc-go/v9/modules/core/exported"
"github.com/cosmos/ibc-go/v9/modules/core/types"
Expand Down Expand Up @@ -145,6 +146,20 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
0,
channeltypes.Params{UpgradeTimeout: channeltypes.DefaultTimeout},
),
ChannelV2Genesis: channelv2types.NewGenesisState(
[]channelv2types.PacketState{
channelv2types.NewPacketState(channel2, 1, []byte("ack")),
},
[]channelv2types.PacketState{
channelv2types.NewPacketState(channel2, 1, []byte("")),
},
[]channelv2types.PacketState{
channelv2types.NewPacketState(channel1, 1, []byte("commit_hash")),
},
[]channelv2types.PacketSequence{
channelv2types.NewPacketSequence(channel1, 1),
},
),
},
expError: nil,
},
Expand Down Expand Up @@ -172,6 +187,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
2,
),
ConnectionGenesis: connectiontypes.DefaultGenesisState(),
ChannelV2Genesis: channelv2types.DefaultGenesisState(),
},
expError: errors.New("genesis metadata key cannot be empty"),
},
Expand All @@ -189,6 +205,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
0,
connectiontypes.Params{},
),
ChannelV2Genesis: channelv2types.DefaultGenesisState(),
},
expError: errors.New("invalid connection"),
},
Expand All @@ -202,6 +219,21 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
channeltypes.NewPacketState("(portID)", channel1, 1, []byte("ack")),
},
},
ChannelV2Genesis: channelv2types.DefaultGenesisState(),
},
expError: errors.New("invalid acknowledgement"),
},
{
name: "invalid channel v2 genesis",
genState: &types.GenesisState{
ClientGenesis: clienttypes.DefaultGenesisState(),
ConnectionGenesis: connectiontypes.DefaultGenesisState(),
ChannelGenesis: channeltypes.DefaultGenesisState(),
ChannelV2Genesis: channelv2types.GenesisState{
Acknowledgements: []channelv2types.PacketState{
channelv2types.NewPacketState(channel1, 1, nil),
},
},
},
expError: errors.New("invalid acknowledgement"),
},
Expand Down Expand Up @@ -305,6 +337,20 @@ func (suite *IBCTestSuite) TestInitGenesis() {
0,
channeltypes.Params{UpgradeTimeout: channeltypes.DefaultTimeout},
),
ChannelV2Genesis: channelv2types.NewGenesisState(
[]channelv2types.PacketState{
channelv2types.NewPacketState(channel2, 1, []byte("ack")),
},
[]channelv2types.PacketState{
channelv2types.NewPacketState(channel2, 1, []byte("")),
},
[]channelv2types.PacketState{
channelv2types.NewPacketState(channel1, 1, []byte("commit_hash")),
},
[]channelv2types.PacketSequence{
channelv2types.NewPacketSequence(channel1, 1),
},
),
},
},
}
Expand Down
Loading
Loading