Skip to content

Commit 4f3c2d8

Browse files
Alex | Interchain LabsAdityaSripal
andauthored
feat: channel-v2 genesis (#7933)
* add * fix and wire to core * sequence check and cleanup * have more than one client in state on tests * refactor * lint fix --------- Co-authored-by: Aditya Sripal <14364734+AdityaSripal@users.noreply.github.com>
1 parent 5ee5783 commit 4f3c2d8

File tree

12 files changed

+355
-27
lines changed

12 files changed

+355
-27
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package channelv2
2+
3+
import (
4+
"context"
5+
6+
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/keeper"
7+
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
8+
)
9+
10+
func InitGenesis(ctx context.Context, k *keeper.Keeper, gs types.GenesisState) {
11+
// set acks
12+
for _, ack := range gs.Acknowledgements {
13+
k.SetPacketAcknowledgement(ctx, ack.ClientId, ack.Sequence, ack.Data)
14+
}
15+
16+
// set commits
17+
for _, commitment := range gs.Commitments {
18+
k.SetPacketCommitment(ctx, commitment.ClientId, commitment.Sequence, commitment.Data)
19+
}
20+
21+
// set receipts
22+
for _, receipt := range gs.Receipts {
23+
k.SetPacketReceipt(ctx, receipt.ClientId, receipt.Sequence)
24+
}
25+
26+
// set send sequences
27+
for _, seq := range gs.SendSequences {
28+
k.SetNextSequenceSend(ctx, seq.ClientId, seq.Sequence)
29+
}
30+
}
31+
32+
func ExportGenesis(ctx context.Context, k *keeper.Keeper) types.GenesisState {
33+
clientStates := k.ClientKeeper.GetAllGenesisClients(ctx)
34+
gs := types.GenesisState{
35+
Acknowledgements: make([]types.PacketState, 0),
36+
Commitments: make([]types.PacketState, 0),
37+
Receipts: make([]types.PacketState, 0),
38+
SendSequences: make([]types.PacketSequence, 0),
39+
}
40+
for _, clientState := range clientStates {
41+
acks := k.GetAllPacketAcknowledgementsForClient(ctx, clientState.ClientId)
42+
gs.Acknowledgements = append(gs.Acknowledgements, acks...)
43+
44+
comms := k.GetAllPacketCommitmentsForClient(ctx, clientState.ClientId)
45+
gs.Commitments = append(gs.Commitments, comms...)
46+
47+
receipts := k.GetAllPacketReceiptsForClient(ctx, clientState.ClientId)
48+
gs.Receipts = append(gs.Receipts, receipts...)
49+
50+
seq, ok := k.GetNextSequenceSend(ctx, clientState.ClientId)
51+
if ok {
52+
gs.SendSequences = append(gs.SendSequences, types.NewPacketSequence(clientState.ClientId, seq))
53+
}
54+
}
55+
56+
return gs
57+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package channelv2_test
2+
3+
import (
4+
channelv2 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2"
5+
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
6+
ibctesting "github.com/cosmos/ibc-go/v9/testing"
7+
)
8+
9+
// TestInitExportGenesis tests the import and export flow for the channel v2 keeper.
10+
func (suite *ModuleTestSuite) TestInitExportGenesis() {
11+
path := ibctesting.NewPath(suite.chainA, suite.chainB)
12+
path.SetupV2()
13+
14+
path2 := ibctesting.NewPath(suite.chainA, suite.chainC)
15+
path2.SetupV2()
16+
17+
app := suite.chainA.App
18+
19+
emptyGenesis := types.DefaultGenesisState()
20+
21+
// create a valid genesis state that uses the client keepers existing client IDs
22+
clientStates := app.GetIBCKeeper().ClientKeeper.GetAllGenesisClients(suite.chainA.GetContext())
23+
validGs := types.DefaultGenesisState()
24+
for i, clientState := range clientStates {
25+
ack := types.NewPacketState(clientState.ClientId, uint64(i+1), []byte("ack"))
26+
receipt := types.NewPacketState(clientState.ClientId, uint64(i+1), []byte{byte(0x2)})
27+
commitment := types.NewPacketState(clientState.ClientId, uint64(i+1), []byte("commit_hash"))
28+
seq := types.NewPacketSequence(clientState.ClientId, uint64(i+1))
29+
30+
validGs.Acknowledgements = append(validGs.Acknowledgements, ack)
31+
validGs.Receipts = append(validGs.Receipts, receipt)
32+
validGs.Commitments = append(validGs.Commitments, commitment)
33+
validGs.SendSequences = append(validGs.SendSequences, seq)
34+
emptyGenesis.SendSequences = append(emptyGenesis.SendSequences, seq)
35+
}
36+
37+
tests := []struct {
38+
name string
39+
genState types.GenesisState
40+
}{
41+
{
42+
name: "no modifications genesis",
43+
genState: emptyGenesis,
44+
},
45+
{
46+
name: "valid",
47+
genState: validGs,
48+
},
49+
}
50+
51+
for _, tt := range tests {
52+
suite.Run(tt.name, func() {
53+
channelV2Keeper := app.GetIBCKeeper().ChannelKeeperV2
54+
55+
channelv2.InitGenesis(suite.chainA.GetContext(), channelV2Keeper, tt.genState)
56+
57+
exported := channelv2.ExportGenesis(suite.chainA.GetContext(), channelV2Keeper)
58+
suite.Require().Equal(tt.genState, exported)
59+
})
60+
}
61+
}

modules/core/04-channel/v2/keeper/keeper.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package keeper
22

33
import (
4+
"bytes"
45
"context"
56

67
corestore "cosmossdk.io/core/store"
78
"cosmossdk.io/log"
9+
storetypes "cosmossdk.io/store/types"
810

911
"github.com/cosmos/cosmos-sdk/codec"
12+
"github.com/cosmos/cosmos-sdk/runtime"
1013
sdk "github.com/cosmos/cosmos-sdk/types"
1114

1215
connectionkeeper "github.com/cosmos/ibc-go/v9/modules/core/03-connection/keeper"
@@ -193,3 +196,58 @@ func (k *Keeper) DeleteAsyncPacket(ctx context.Context, clientID string, sequenc
193196
panic(err)
194197
}
195198
}
199+
200+
// extractSequenceFromKey takes the full store key as well as a packet store prefix and extracts
201+
// the encoded sequence number from the key.
202+
//
203+
// This function panics of the provided key once trimmed is larger than 8 bytes as the expected
204+
// sequence byte length is always 8.
205+
func extractSequenceFromKey(key, storePrefix []byte) uint64 {
206+
sequenceBz := bytes.TrimPrefix(key, storePrefix)
207+
if len(sequenceBz) > 8 {
208+
panic("sequence is too long - expected 8 bytes")
209+
}
210+
return sdk.BigEndianToUint64(sequenceBz)
211+
}
212+
213+
// GetAllPacketCommitmentsForClient returns all stored PacketCommitments objects for a specified
214+
// client ID.
215+
func (k *Keeper) GetAllPacketCommitmentsForClient(ctx context.Context, clientID string) []types.PacketState {
216+
return k.getAllPacketsForClientStore(ctx, clientID, hostv2.PacketCommitmentPrefixKey)
217+
}
218+
219+
// GetAllPacketAcknowledgementsForClient returns all stored PacketAcknowledgements objects for a specified
220+
// client ID.
221+
func (k *Keeper) GetAllPacketAcknowledgementsForClient(ctx context.Context, clientID string) []types.PacketState {
222+
return k.getAllPacketsForClientStore(ctx, clientID, hostv2.PacketAcknowledgementPrefixKey)
223+
}
224+
225+
// GetAllPacketReceiptsForClient returns all stored PacketReceipts objects for a specified
226+
// client ID.
227+
func (k *Keeper) GetAllPacketReceiptsForClient(ctx context.Context, clientID string) []types.PacketState {
228+
return k.getAllPacketsForClientStore(ctx, clientID, hostv2.PacketReceiptPrefixKey)
229+
}
230+
231+
// prefixKeyConstructor is a function that constructs a store key for a specific packet store using the provided
232+
// clientID.
233+
type prefixKeyConstructor func(clientID string) []byte
234+
235+
// getAllPacketsForClientStore gets all PacketState objects for the specified clientID using a provided
236+
// function for constructing the key prefix for the store.
237+
//
238+
// For example, to get all PacketReceipts for a clientID the hostv2.PacketReceiptPrefixKey function can be
239+
// passed to get the PacketReceipt store key prefix.
240+
func (k *Keeper) getAllPacketsForClientStore(ctx context.Context, clientID string, prefixFn prefixKeyConstructor) []types.PacketState {
241+
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
242+
storePrefix := prefixFn(clientID)
243+
iterator := storetypes.KVStorePrefixIterator(store, storePrefix)
244+
245+
var packets []types.PacketState
246+
for ; iterator.Valid(); iterator.Next() {
247+
sequence := extractSequenceFromKey(iterator.Key(), storePrefix)
248+
state := types.NewPacketState(clientID, sequence, iterator.Value())
249+
250+
packets = append(packets, state)
251+
}
252+
return packets
253+
}

modules/core/04-channel/v2/module.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package client
1+
package channelv2
22

33
import (
44
"github.com/spf13/cobra"
@@ -7,7 +7,6 @@ import (
77
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
88
)
99

10-
// Name returns the IBC channel/v2 name
1110
func Name() string {
1211
return types.SubModuleName
1312
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package channelv2_test
2+
3+
import (
4+
"testing"
5+
6+
testifysuite "github.com/stretchr/testify/suite"
7+
8+
ibctesting "github.com/cosmos/ibc-go/v9/testing"
9+
)
10+
11+
func TestModuleTestSuite(t *testing.T) {
12+
testifysuite.Run(t, new(ModuleTestSuite))
13+
}
14+
15+
type ModuleTestSuite struct {
16+
testifysuite.Suite
17+
18+
coordinator *ibctesting.Coordinator
19+
20+
// testing chains used for convenience and readability
21+
chainA *ibctesting.TestChain
22+
chainB *ibctesting.TestChain
23+
chainC *ibctesting.TestChain
24+
}
25+
26+
func (suite *ModuleTestSuite) SetupTest() {
27+
suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3)
28+
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1))
29+
suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2))
30+
suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(3))
31+
}

modules/core/04-channel/v2/types/expected_keepers.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ type ClientKeeper interface {
2525
GetClientConsensusState(ctx context.Context, clientID string, height exported.Height) (exported.ConsensusState, bool)
2626
// GetClientCounterparty returns the counterpartyInfo given a clientID
2727
GetClientCounterparty(ctx context.Context, clientID string) (clienttypes.CounterpartyInfo, bool)
28+
// GetAllGenesisClients returns all the clients in state with their client ids returned as IdentifiedClientState
29+
GetAllGenesisClients(ctx context.Context) clienttypes.IdentifiedClientStates
2830
}

modules/core/24-host/v2/packet_keys.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,28 @@ import (
66
sdk "github.com/cosmos/cosmos-sdk/types"
77
)
88

9+
const (
10+
PacketCommitmentBasePrefix = byte(1)
11+
PacketReceiptBasePrefix = byte(2)
12+
PacketAcknowledgementBasePrefix = byte(3)
13+
)
14+
915
// PacketCommitmentPrefixKey returns the store key prefix under which packet commitments for a particular channel are stored.
1016
// channelID must be a generated identifier, not provided externally so key collisions are not possible.
1117
func PacketCommitmentPrefixKey(channelID string) []byte {
12-
return append([]byte(channelID), byte(1))
18+
return append([]byte(channelID), PacketCommitmentBasePrefix)
1319
}
1420

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

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

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

3945
// PacketAcknowledgementKey returns the store key of under which a packet acknowledgement is stored.

modules/core/genesis.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
client "github.com/cosmos/ibc-go/v9/modules/core/02-client"
77
connection "github.com/cosmos/ibc-go/v9/modules/core/03-connection"
88
channel "github.com/cosmos/ibc-go/v9/modules/core/04-channel"
9+
channelv2 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2"
910
"github.com/cosmos/ibc-go/v9/modules/core/keeper"
1011
"github.com/cosmos/ibc-go/v9/modules/core/types"
1112
)
@@ -16,6 +17,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs *types.GenesisState) {
1617
client.InitGenesis(ctx, k.ClientKeeper, gs.ClientGenesis)
1718
connection.InitGenesis(ctx, k.ConnectionKeeper, gs.ConnectionGenesis)
1819
channel.InitGenesis(ctx, k.ChannelKeeper, gs.ChannelGenesis)
20+
channelv2.InitGenesis(ctx, k.ChannelKeeperV2, gs.ChannelV2Genesis)
1921
}
2022

2123
// ExportGenesis returns the ibc exported genesis.
@@ -24,5 +26,6 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
2426
ClientGenesis: client.ExportGenesis(ctx, k.ClientKeeper),
2527
ConnectionGenesis: connection.ExportGenesis(ctx, k.ConnectionKeeper),
2628
ChannelGenesis: channel.ExportGenesis(ctx, k.ChannelKeeper),
29+
ChannelV2Genesis: channelv2.ExportGenesis(ctx, k.ChannelKeeperV2),
2730
}
2831
}

modules/core/genesis_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types"
1414
connectiontypes "github.com/cosmos/ibc-go/v9/modules/core/03-connection/types"
1515
channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types"
16+
channelv2types "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
1617
commitmenttypes "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types"
1718
"github.com/cosmos/ibc-go/v9/modules/core/exported"
1819
"github.com/cosmos/ibc-go/v9/modules/core/types"
@@ -145,6 +146,20 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
145146
0,
146147
channeltypes.Params{UpgradeTimeout: channeltypes.DefaultTimeout},
147148
),
149+
ChannelV2Genesis: channelv2types.NewGenesisState(
150+
[]channelv2types.PacketState{
151+
channelv2types.NewPacketState(channel2, 1, []byte("ack")),
152+
},
153+
[]channelv2types.PacketState{
154+
channelv2types.NewPacketState(channel2, 1, []byte("")),
155+
},
156+
[]channelv2types.PacketState{
157+
channelv2types.NewPacketState(channel1, 1, []byte("commit_hash")),
158+
},
159+
[]channelv2types.PacketSequence{
160+
channelv2types.NewPacketSequence(channel1, 1),
161+
},
162+
),
148163
},
149164
expError: nil,
150165
},
@@ -172,6 +187,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
172187
2,
173188
),
174189
ConnectionGenesis: connectiontypes.DefaultGenesisState(),
190+
ChannelV2Genesis: channelv2types.DefaultGenesisState(),
175191
},
176192
expError: errors.New("genesis metadata key cannot be empty"),
177193
},
@@ -189,6 +205,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
189205
0,
190206
connectiontypes.Params{},
191207
),
208+
ChannelV2Genesis: channelv2types.DefaultGenesisState(),
192209
},
193210
expError: errors.New("invalid connection"),
194211
},
@@ -202,6 +219,21 @@ func (suite *IBCTestSuite) TestValidateGenesis() {
202219
channeltypes.NewPacketState("(portID)", channel1, 1, []byte("ack")),
203220
},
204221
},
222+
ChannelV2Genesis: channelv2types.DefaultGenesisState(),
223+
},
224+
expError: errors.New("invalid acknowledgement"),
225+
},
226+
{
227+
name: "invalid channel v2 genesis",
228+
genState: &types.GenesisState{
229+
ClientGenesis: clienttypes.DefaultGenesisState(),
230+
ConnectionGenesis: connectiontypes.DefaultGenesisState(),
231+
ChannelGenesis: channeltypes.DefaultGenesisState(),
232+
ChannelV2Genesis: channelv2types.GenesisState{
233+
Acknowledgements: []channelv2types.PacketState{
234+
channelv2types.NewPacketState(channel1, 1, nil),
235+
},
236+
},
205237
},
206238
expError: errors.New("invalid acknowledgement"),
207239
},
@@ -305,6 +337,20 @@ func (suite *IBCTestSuite) TestInitGenesis() {
305337
0,
306338
channeltypes.Params{UpgradeTimeout: channeltypes.DefaultTimeout},
307339
),
340+
ChannelV2Genesis: channelv2types.NewGenesisState(
341+
[]channelv2types.PacketState{
342+
channelv2types.NewPacketState(channel2, 1, []byte("ack")),
343+
},
344+
[]channelv2types.PacketState{
345+
channelv2types.NewPacketState(channel2, 1, []byte("")),
346+
},
347+
[]channelv2types.PacketState{
348+
channelv2types.NewPacketState(channel1, 1, []byte("commit_hash")),
349+
},
350+
[]channelv2types.PacketSequence{
351+
channelv2types.NewPacketSequence(channel1, 1),
352+
},
353+
),
308354
},
309355
},
310356
}

0 commit comments

Comments
 (0)