ibc/03-connection: import export GenesisState (#6067)

* ibc/03-connection: import export GenesisState

* add connection paths

* add genesis to ibc module root package

* genesis tests

* iterator and fixes

* changelog

* lint fixes

* godoc

* revert validation
This commit is contained in:
Federico Kunze 2020-04-24 11:52:07 -04:00 committed by GitHub
parent bdcc04347c
commit e9b5fc9716
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 418 additions and 60 deletions

View File

@ -223,6 +223,7 @@ functionality that requires an online connection.
* (x/auth/ante) [\#6040](https://github.com/cosmos/cosmos-sdk/pull/6040) `AccountKeeper` interface used for `NewAnteHandler` and handler's decorators to add support of using custom `AccountKeeper` implementations.
* (simulation) [\#6002](https://github.com/cosmos/cosmos-sdk/pull/6002) Add randomized consensus params into simulation.
* (x/staking) [\#6059](https://github.com/cosmos/cosmos-sdk/pull/6059) Updated `HistoricalEntries` parameter default to 100.
* (x/ibc) [\#5948](https://github.com/cosmos/cosmos-sdk/issues/5948) Add `InitGenesis` and `ExportGenesis` functions for `ibc` module.
## [v0.38.3] - 2020-04-09

View File

@ -46,6 +46,9 @@ var (
GetCompatibleVersions = types.GetCompatibleVersions
LatestVersion = types.LatestVersion
PickVersion = types.PickVersion
NewConnectionPaths = types.NewConnectionPaths
DefaultGenesisState = types.DefaultGenesisState
NewGenesisState = types.NewGenesisState
// variable aliases
SubModuleCdc = types.SubModuleCdc
@ -69,4 +72,6 @@ type (
ConnectionResponse = types.ConnectionResponse
ClientConnectionsResponse = types.ClientConnectionsResponse
QueryClientConnectionsParams = types.QueryClientConnectionsParams
GenesisState = types.GenesisState
ConnectionPaths = types.ConnectionPaths
)

View File

@ -17,7 +17,7 @@ import (
// QueryAllConnections returns all the connections. It _does not_ return
// any merkle proof.
func QueryAllConnections(cliCtx context.CLIContext, page, limit int) ([]types.IdentifiedConnectionEnd, int64, error) {
func QueryAllConnections(cliCtx context.CLIContext, page, limit int) ([]types.ConnectionEnd, int64, error) {
params := types.NewQueryAllConnectionsParams(page, limit)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
@ -30,7 +30,7 @@ func QueryAllConnections(cliCtx context.CLIContext, page, limit int) ([]types.Id
return nil, 0, err
}
var connections []types.IdentifiedConnectionEnd
var connections []types.ConnectionEnd
err = cliCtx.Codec.UnmarshalJSON(res, &connections)
if err != nil {
return nil, 0, fmt.Errorf("failed to unmarshal connections: %w", err)

View File

@ -9,6 +9,7 @@ import (
// ConnectionI describes the required methods for a connection.
type ConnectionI interface {
GetState() State
GetID() string
GetClientID() string
GetCounterparty() CounterpartyI
GetVersions() []string

View File

@ -0,0 +1,24 @@
package connection
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// InitGenesis initializes the ibc connection submodule's state from a provided genesis
// state.
func InitGenesis(ctx sdk.Context, k Keeper, gs GenesisState) {
for _, connection := range gs.Connections {
k.SetConnection(ctx, connection.ID, connection)
}
for _, connPaths := range gs.ClientConnectionPaths {
k.SetClientConnectionPaths(ctx, connPaths.ClientID, connPaths.Paths)
}
}
// ExportGenesis returns the ibc connection submodule's exported genesis.
func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
return GenesisState{
Connections: k.GetAllConnections(ctx),
ClientConnectionPaths: k.GetAllClientConnectionPaths(ctx),
}
}

View File

@ -28,7 +28,7 @@ func (k Keeper) ConnOpenInit(
}
// connection defines chain A's ConnectionEnd
connection := types.NewConnectionEnd(exported.INIT, clientID, counterparty, types.GetCompatibleVersions())
connection := types.NewConnectionEnd(exported.INIT, connectionID, clientID, counterparty, types.GetCompatibleVersions())
k.SetConnection(ctx, connectionID, connection)
if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil {
@ -69,14 +69,14 @@ func (k Keeper) ConnOpenTry(
// NOTE: chain A's counterparty is chain B (i.e where this code is executed)
prefix := k.GetCommitmentPrefix()
expectedCounterparty := types.NewCounterparty(clientID, connectionID, prefix)
expectedConnection := types.NewConnectionEnd(exported.INIT, counterparty.ClientID, expectedCounterparty, counterpartyVersions)
expectedConnection := types.NewConnectionEnd(exported.INIT, counterparty.ConnectionID, counterparty.ClientID, expectedCounterparty, counterpartyVersions)
// chain B picks a version from Chain A's available versions that is compatible
// with the supported IBC versions
version := types.PickVersion(counterpartyVersions, types.GetCompatibleVersions())
// connection defines chain B's ConnectionEnd
connection := types.NewConnectionEnd(exported.UNINITIALIZED, clientID, counterparty, []string{version})
connection := types.NewConnectionEnd(exported.UNINITIALIZED, connectionID, clientID, counterparty, []string{version})
// Check that ChainA committed expectedConnectionEnd to its state
if err := k.VerifyConnectionState(
@ -165,7 +165,7 @@ func (k Keeper) ConnOpenAck(
prefix := k.GetCommitmentPrefix()
expectedCounterparty := types.NewCounterparty(connection.ClientID, connectionID, prefix)
expectedConnection := types.NewConnectionEnd(exported.TRYOPEN, connection.Counterparty.ClientID, expectedCounterparty, []string{version})
expectedConnection := types.NewConnectionEnd(exported.TRYOPEN, connection.Counterparty.ConnectionID, connection.Counterparty.ClientID, expectedCounterparty, []string{version})
// Ensure that ChainB stored expected connectionEnd in its state during ConnOpenTry
if err := k.VerifyConnectionState(
@ -216,7 +216,7 @@ func (k Keeper) ConnOpenConfirm(
prefix := k.GetCommitmentPrefix()
expectedCounterparty := types.NewCounterparty(connection.ClientID, connectionID, prefix)
expectedConnection := types.NewConnectionEnd(exported.OPEN, connection.Counterparty.ClientID, expectedCounterparty, connection.Versions)
expectedConnection := types.NewConnectionEnd(exported.OPEN, connection.Counterparty.ConnectionID, connection.Counterparty.ClientID, expectedCounterparty, connection.Versions)
// Check that connection on ChainA is open
if err := k.VerifyConnectionState(

View File

@ -25,7 +25,7 @@ func (suite *KeeperTestSuite) TestConnOpenInit() {
{"couldn't add connection to client", func() {}, false},
}
counterparty := connection.NewCounterparty(testClientIDA, testConnectionIDB, suite.chainA.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix())
counterparty := connection.NewCounterparty(testClientIDB, testConnectionIDB, suite.chainA.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix())
for i, tc := range testCases {
tc := tc

View File

@ -8,6 +8,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported"
@ -101,10 +102,29 @@ func (k Keeper) SetClientConnectionPaths(ctx sdk.Context, clientID string, paths
store.Set(ibctypes.KeyClientConnections(clientID), bz)
}
// GetAllClientConnectionPaths returns all stored clients connection id paths. It
// will ignore the clients that haven't initialized a connection handshake since
// no paths are stored.
func (k Keeper) GetAllClientConnectionPaths(ctx sdk.Context) []types.ConnectionPaths {
var allConnectionPaths []types.ConnectionPaths
k.clientKeeper.IterateClients(ctx, func(cs clientexported.ClientState) bool {
paths, found := k.GetClientConnectionPaths(ctx, cs.GetID())
if !found {
// continue when connection handshake is not initialized
return false
}
connPaths := types.NewConnectionPaths(cs.GetID(), paths)
allConnectionPaths = append(allConnectionPaths, connPaths)
return false
})
return allConnectionPaths
}
// IterateConnections provides an iterator over all ConnectionEnd objects.
// For each ConnectionEnd, cb will be called. If the cb returns true, the
// iterator will close and stop.
func (k Keeper) IterateConnections(ctx sdk.Context, cb func(types.IdentifiedConnectionEnd) bool) {
func (k Keeper) IterateConnections(ctx sdk.Context, cb func(types.ConnectionEnd) bool) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ibctypes.KeyConnectionPrefix)
@ -112,22 +132,16 @@ func (k Keeper) IterateConnections(ctx sdk.Context, cb func(types.IdentifiedConn
for ; iterator.Valid(); iterator.Next() {
var connection types.ConnectionEnd
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &connection)
identifier := string(iterator.Key()[len(ibctypes.KeyConnectionPrefix)+1:])
conn := types.IdentifiedConnectionEnd{
Connection: connection,
Identifier: identifier,
}
if cb(conn) {
if cb(connection) {
break
}
}
}
// GetAllConnections returns all stored ConnectionEnd objects.
func (k Keeper) GetAllConnections(ctx sdk.Context) (connections []types.IdentifiedConnectionEnd) {
k.IterateConnections(ctx, func(connection types.IdentifiedConnectionEnd) bool {
func (k Keeper) GetAllConnections(ctx sdk.Context) (connections []types.ConnectionEnd) {
k.IterateConnections(ctx, func(connection types.ConnectionEnd) bool {
connections = append(connections, connection)
return false
})

View File

@ -12,6 +12,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
"github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported"
"github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported"
@ -36,7 +37,7 @@ const (
trustingPeriod time.Duration = time.Hour * 24 * 7 * 2
ubdPeriod time.Duration = time.Hour * 24 * 7 * 3
maxClockDrift time.Duration = time.Second * 10
maxClockDrift time.Duration = time.Second * 10
nextTimestamp = 10 // increment used for the next header's timestamp
)
@ -88,7 +89,7 @@ func (suite *KeeperTestSuite) TestSetAndGetConnection() {
suite.Require().False(existed)
counterparty := types.NewCounterparty(testClientIDA, testConnectionIDA, suite.chainA.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix())
expConn := types.NewConnectionEnd(exported.INIT, testClientIDB, counterparty, types.GetCompatibleVersions())
expConn := types.NewConnectionEnd(exported.INIT, testConnectionIDB, testClientIDB, counterparty, types.GetCompatibleVersions())
suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), testConnectionIDA, expConn)
conn, existed := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetConnection(suite.chainA.GetContext(), testConnectionIDA)
suite.Require().True(existed)
@ -111,25 +112,46 @@ func (suite KeeperTestSuite) TestGetAllConnections() {
counterparty2 := types.NewCounterparty(testClientIDB, testConnectionIDB, suite.chainA.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix())
counterparty3 := types.NewCounterparty(testClientID3, testConnectionID3, suite.chainA.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix())
conn1 := types.NewConnectionEnd(exported.INIT, testClientIDA, counterparty3, types.GetCompatibleVersions())
conn2 := types.NewConnectionEnd(exported.INIT, testClientIDB, counterparty1, types.GetCompatibleVersions())
conn3 := types.NewConnectionEnd(exported.UNINITIALIZED, testClientID3, counterparty2, types.GetCompatibleVersions())
conn1 := types.NewConnectionEnd(exported.INIT, testConnectionIDA, testClientIDA, counterparty3, types.GetCompatibleVersions())
conn2 := types.NewConnectionEnd(exported.INIT, testConnectionIDB, testClientIDB, counterparty1, types.GetCompatibleVersions())
conn3 := types.NewConnectionEnd(exported.UNINITIALIZED, testConnectionID3, testClientID3, counterparty2, types.GetCompatibleVersions())
expConnections := []types.IdentifiedConnectionEnd{
{Connection: conn1, Identifier: testConnectionIDA},
{Connection: conn2, Identifier: testConnectionIDB},
{Connection: conn3, Identifier: testConnectionID3},
expConnections := []types.ConnectionEnd{conn1, conn2, conn3}
for i := range expConnections {
suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), expConnections[i].ID, expConnections[i])
}
suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), testConnectionIDA, conn1)
suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), testConnectionIDB, conn2)
suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), testConnectionID3, conn3)
connections := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetAllConnections(suite.chainA.GetContext())
suite.Require().Len(connections, len(expConnections))
suite.Require().ElementsMatch(expConnections, connections)
}
func (suite KeeperTestSuite) TestGetAllClientConnectionPaths() {
clients := []clientexported.ClientState{
ibctmtypes.NewClientState(testClientIDA, trustingPeriod, ubdPeriod, maxClockDrift, ibctmtypes.Header{}),
ibctmtypes.NewClientState(testClientIDB, trustingPeriod, ubdPeriod, maxClockDrift, ibctmtypes.Header{}),
ibctmtypes.NewClientState(testClientID3, trustingPeriod, ubdPeriod, maxClockDrift, ibctmtypes.Header{}),
}
for i := range clients {
suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clients[i])
}
expPaths := []types.ConnectionPaths{
types.NewConnectionPaths(testClientIDA, []string{ibctypes.ConnectionPath(testConnectionIDA)}),
types.NewConnectionPaths(testClientIDB, []string{ibctypes.ConnectionPath(testConnectionIDB), ibctypes.ConnectionPath(testConnectionID3)}),
}
for i := range expPaths {
suite.chainA.App.IBCKeeper.ConnectionKeeper.SetClientConnectionPaths(suite.chainA.GetContext(), expPaths[i].ClientID, expPaths[i].Paths)
}
connPaths := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetAllClientConnectionPaths(suite.chainA.GetContext())
suite.Require().Len(connPaths, 2)
suite.Require().Equal(connPaths, expPaths)
}
// TestGetTimestampAtHeight verifies if the clients on each chain return the correct timestamp
// for the other chain.
func (suite *KeeperTestSuite) TestGetTimestampAtHeight() {
@ -338,6 +360,7 @@ func (chain *TestChain) createConnection(
counterparty := types.NewCounterparty(counterpartyClientID, counterpartyConnID, chain.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix())
connection := types.ConnectionEnd{
State: state,
ID: connID,
ClientID: clientID,
Counterparty: counterparty,
Versions: types.GetCompatibleVersions(),

View File

@ -22,7 +22,7 @@ func QuerierConnections(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byt
start, end := client.Paginate(len(connections), params.Page, params.Limit, 100)
if start < 0 || end < 0 {
connections = []types.IdentifiedConnectionEnd{}
connections = []types.ConnectionEnd{}
} else {
connections = connections[start:end]
}

View File

@ -27,7 +27,7 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() {
suite.chainA.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix(),
)
connection1 := types.NewConnectionEnd(
exported.UNINITIALIZED, testClientIDB, counterparty,
exported.UNINITIALIZED, testConnectionIDB, testClientIDB, counterparty,
types.GetCompatibleVersions(),
)
@ -135,8 +135,8 @@ func (suite *KeeperTestSuite) TestVerifyConnectionState() {
}
// Create B's connection to A
counterparty := types.NewCounterparty(testClientIDB, testConnectionIDA, commitmenttypes.NewMerklePrefix([]byte("ibc")))
connection := types.NewConnectionEnd(exported.UNINITIALIZED, testClientIDA, counterparty, []string{"1.0.0"})
counterparty := types.NewCounterparty(testClientIDB, testConnectionIDB, commitmenttypes.NewMerklePrefix([]byte("ibc")))
connection := types.NewConnectionEnd(exported.UNINITIALIZED, testConnectionIDA, testClientIDA, counterparty, []string{"1.0.0"})
// Ensure chain B can verify connection exists in chain A
err := suite.chainB.App.IBCKeeper.ConnectionKeeper.VerifyConnectionState(
suite.chainB.GetContext(), connection, proofHeight+1, proof, testConnectionIDA, expectedConnection,
@ -161,7 +161,7 @@ func (suite *KeeperTestSuite) TestVerifyChannelState() {
)
connection := types.NewConnectionEnd(
exported.UNINITIALIZED, testClientIDA, counterparty,
exported.UNINITIALIZED, testConnectionIDA, testClientIDA, counterparty,
types.GetCompatibleVersions(),
)

View File

@ -20,6 +20,7 @@ var _ exported.ConnectionI = ConnectionEnd{}
// between two chains.
type ConnectionEnd struct {
State exported.State `json:"state" yaml:"state"`
ID string `json:"id" yaml:"id"`
ClientID string `json:"client_id" yaml:"client_id"`
// Counterparty chain associated with this connection.
@ -30,9 +31,10 @@ type ConnectionEnd struct {
}
// NewConnectionEnd creates a new ConnectionEnd instance.
func NewConnectionEnd(state exported.State, clientID string, counterparty Counterparty, versions []string) ConnectionEnd {
func NewConnectionEnd(state exported.State, connectionID, clientID string, counterparty Counterparty, versions []string) ConnectionEnd {
return ConnectionEnd{
State: state,
ID: connectionID,
ClientID: clientID,
Counterparty: counterparty,
Versions: versions,
@ -44,6 +46,11 @@ func (c ConnectionEnd) GetState() exported.State {
return c.State
}
// GetID implements the Connection interface
func (c ConnectionEnd) GetID() string {
return c.ID
}
// GetClientID implements the Connection interface
func (c ConnectionEnd) GetClientID() string {
return c.ClientID
@ -59,8 +66,13 @@ func (c ConnectionEnd) GetVersions() []string {
return c.Versions
}
// ValidateBasic implements the Connection interface
// ValidateBasic implements the Connection interface.
// NOTE: the protocol supports that the connection and client IDs match the
// counterparty's.
func (c ConnectionEnd) ValidateBasic() error {
if err := host.DefaultConnectionIdentifierValidator(c.ID); err != nil {
return sdkerrors.Wrapf(err, "invalid connection ID: %s", c.ID)
}
if err := host.DefaultClientIdentifierValidator(c.ClientID); err != nil {
return sdkerrors.Wrapf(err, "invalid client ID: %s", c.ClientID)
}

View File

@ -9,6 +9,13 @@ import (
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
)
var (
connectionID = "connectionidone"
clientID = "clientidone"
connectionID2 = "connectionidtwo"
clientID2 = "clientidtwo"
)
func TestConnectionValidateBasic(t *testing.T) {
testCases := []struct {
name string
@ -17,27 +24,32 @@ func TestConnectionValidateBasic(t *testing.T) {
}{
{
"valid connection",
ConnectionEnd{exported.INIT, "clientidone", Counterparty{"clientidtwo", "connectionidone", commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{"1.0.0"}},
ConnectionEnd{exported.INIT, connectionID, clientID, Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{"1.0.0"}},
true,
},
{
"invalid connection id",
ConnectionEnd{exported.INIT, "connectionIDONE", clientID, Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{"1.0.0"}},
false,
},
{
"invalid client id",
ConnectionEnd{exported.INIT, "ClientIDTwo", Counterparty{"clientidtwo", "connectionidone", commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{"1.0.0"}},
ConnectionEnd{exported.INIT, connectionID, "ClientIDTwo", Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{"1.0.0"}},
false,
},
{
"empty versions",
ConnectionEnd{exported.INIT, "clientidone", Counterparty{"clientidtwo", "connectionidone", commitmenttypes.NewMerklePrefix([]byte("prefix"))}, nil},
ConnectionEnd{exported.INIT, connectionID, clientID, Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, nil},
false,
},
{
"invalid version",
ConnectionEnd{exported.INIT, "clientidone", Counterparty{"clientidtwo", "connectionidone", commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{""}},
ConnectionEnd{exported.INIT, connectionID, clientID, Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{""}},
false,
},
{
"invalid counterparty",
ConnectionEnd{exported.INIT, "clientidone", Counterparty{"clientidtwo", "connectionidone", nil}, []string{"1.0.0"}},
ConnectionEnd{exported.INIT, connectionID, clientID, Counterparty{clientID2, connectionID2, nil}, []string{"1.0.0"}},
false,
},
}
@ -60,10 +72,10 @@ func TestCounterpartyValidateBasic(t *testing.T) {
counterparty Counterparty
expPass bool
}{
{"valid counterparty", Counterparty{"clientidone", "connectionidone", commitmenttypes.NewMerklePrefix([]byte("prefix"))}, true},
{"valid counterparty", Counterparty{"clientidone", connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, true},
{"invalid client id", Counterparty{"InvalidClient", "channelidone", commitmenttypes.NewMerklePrefix([]byte("prefix"))}, false},
{"invalid connection id", Counterparty{"clientidone", "InvalidConnection", commitmenttypes.NewMerklePrefix([]byte("prefix"))}, false},
{"invalid prefix", Counterparty{"clientidone", "connectionidone", nil}, false},
{"invalid prefix", Counterparty{"clientidone", connectionID2, nil}, false},
}
for i, tc := range testCases {

View File

@ -10,4 +10,5 @@ type ClientKeeper interface {
GetClientState(ctx sdk.Context, clientID string) (clientexported.ClientState, bool)
GetClientConsensusState(ctx sdk.Context, clientID string, height uint64) (clientexported.ConsensusState, bool)
GetSelfConsensusState(ctx sdk.Context, height uint64) (clientexported.ConsensusState, bool)
IterateClients(ctx sdk.Context, cb func(clientexported.ClientState) bool)
}

View File

@ -0,0 +1,68 @@
package types
import (
"fmt"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
// ConnectionPaths define all the connection paths for a given client state.
type ConnectionPaths struct {
ClientID string `json:"client_id" yaml:"client_id"`
Paths []string `json:"paths" yaml:"paths"`
}
// NewConnectionPaths creates a ConnectionPaths instance.
func NewConnectionPaths(id string, paths []string) ConnectionPaths {
return ConnectionPaths{
ClientID: id,
Paths: paths,
}
}
// GenesisState defines the ibc connection submodule's genesis state.
type GenesisState struct {
Connections []ConnectionEnd `json:"connections" yaml:"connections"`
ClientConnectionPaths []ConnectionPaths `json:"client_connection_paths" yaml:"client_connection_paths"`
}
// NewGenesisState creates a GenesisState instance.
func NewGenesisState(
connections []ConnectionEnd, connPaths []ConnectionPaths,
) GenesisState {
return GenesisState{
Connections: connections,
ClientConnectionPaths: connPaths,
}
}
// DefaultGenesisState returns the ibc connection submodule's default genesis state.
func DefaultGenesisState() GenesisState {
return GenesisState{
Connections: []ConnectionEnd{},
ClientConnectionPaths: []ConnectionPaths{},
}
}
// Validate performs basic genesis state validation returning an error upon any
// failure.
func (gs GenesisState) Validate() error {
for i, conn := range gs.Connections {
if err := conn.ValidateBasic(); err != nil {
return fmt.Errorf("invalid connection %d: %w", i, err)
}
}
for i, conPaths := range gs.ClientConnectionPaths {
if err := host.DefaultClientIdentifierValidator(conPaths.ClientID); err != nil {
return fmt.Errorf("invalid client connection path %d: %w", i, err)
}
for _, path := range conPaths.Paths {
if err := host.DefaultPathValidator(path); err != nil {
return fmt.Errorf("invalid client connection path %d: %w", i, err)
}
}
}
return nil
}

View File

@ -0,0 +1,84 @@
package types
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
)
func TestValidateGenesis(t *testing.T) {
testCases := []struct {
name string
genState GenesisState
expPass bool
}{
{
name: "default",
genState: DefaultGenesisState(),
expPass: true,
},
{
name: "valid genesis",
genState: NewGenesisState(
[]ConnectionEnd{
{exported.INIT, connectionID, clientID, Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{"1.0.0"}},
},
[]ConnectionPaths{
{clientID, []string{ibctypes.ConnectionPath(connectionID)}},
},
),
expPass: true,
},
{
name: "invalid connection",
genState: NewGenesisState(
[]ConnectionEnd{
NewConnectionEnd(exported.INIT, connectionID, "CLIENTIDONE", Counterparty{clientID, connectionID, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{"1.0.0"}),
},
[]ConnectionPaths{
{clientID, []string{ibctypes.ConnectionPath(connectionID)}},
},
),
expPass: false,
},
{
name: "invalid client id",
genState: NewGenesisState(
[]ConnectionEnd{
{exported.INIT, connectionID, clientID, Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{"1.0.0"}},
},
[]ConnectionPaths{
{"CLIENTIDONE", []string{ibctypes.ConnectionPath(connectionID)}},
},
),
expPass: false,
},
{
name: "invalid path",
genState: NewGenesisState(
[]ConnectionEnd{
{exported.INIT, connectionID, clientID, Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []string{"1.0.0"}},
},
[]ConnectionPaths{
{clientID, []string{connectionID}},
},
),
expPass: false,
},
}
for _, tc := range testCases {
tc := tc
err := tc.genState.Validate()
if tc.expPass {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
}
}

View File

@ -15,16 +15,10 @@ const (
QueryClientConnections = "client_connections"
)
// IdentifiedConnectionEnd defines the union of a connection end & an identifier.
type IdentifiedConnectionEnd struct {
Connection ConnectionEnd `json:"connection_end" yaml:"connection_end"`
Identifier string `json:"identifier" yaml:"identifier"`
}
// ConnectionResponse defines the client query response for a connection which
// also includes a proof and the height from which the proof was retrieved.
type ConnectionResponse struct {
Connection IdentifiedConnectionEnd `json:"connection" yaml:"connection"`
Connection ConnectionEnd `json:"connection" yaml:"connection"`
Proof commitmenttypes.MerkleProof `json:"proof,omitempty" yaml:"proof,omitempty"`
ProofPath commitmenttypes.MerklePath `json:"proof_path,omitempty" yaml:"proof_path,omitempty"`
ProofHeight uint64 `json:"proof_height,omitempty" yaml:"proof_height,omitempty"`
@ -35,7 +29,7 @@ func NewConnectionResponse(
connectionID string, connection ConnectionEnd, proof *merkle.Proof, height int64,
) ConnectionResponse {
return ConnectionResponse{
Connection: IdentifiedConnectionEnd{connection, connectionID},
Connection: connection,
Proof: commitmenttypes.MerkleProof{Proof: proof},
ProofPath: commitmenttypes.NewMerklePath(strings.Split(ibctypes.ConnectionPath(connectionID), "/")),
ProofHeight: uint64(height),

View File

@ -92,7 +92,7 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() {
func (suite *TendermintTestSuite) TestVerifyConnectionState() {
counterparty := connection.NewCounterparty("clientB", testConnectionID, commitmenttypes.NewMerklePrefix([]byte("ibc")))
conn := connection.NewConnectionEnd(connectionexported.OPEN, "clientA", counterparty, []string{"1.0.0"})
conn := connection.NewConnectionEnd(connectionexported.OPEN, testConnectionID, "clientA", counterparty, []string{"1.0.0"})
testCases := []struct {
name string

View File

@ -58,7 +58,7 @@ func (suite *LocalhostTestSuite) TestVerifyClientConsensusState() {
func (suite *LocalhostTestSuite) TestVerifyConnectionState() {
counterparty := connection.NewCounterparty("clientB", testConnectionID, commitmenttypes.NewMerklePrefix([]byte("ibc")))
conn := connection.NewConnectionEnd(connectionexported.OPEN, "clientA", counterparty, []string{"1.0.0"})
conn := connection.NewConnectionEnd(connectionexported.OPEN, testConnectionID, "clientA", counterparty, []string{"1.0.0"})
testCases := []struct {
name string

37
x/ibc/genesis.go Normal file
View File

@ -0,0 +1,37 @@
package ibc
import (
sdk "github.com/cosmos/cosmos-sdk/types"
connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection"
)
// GenesisState defines the ibc module's genesis state.
type GenesisState struct {
ConnectionGenesis connection.GenesisState `json:"connection_genesis" yaml:"connection_genesis"`
}
// DefaultGenesisState returns the ibc module's default genesis state.
func DefaultGenesisState() GenesisState {
return GenesisState{
ConnectionGenesis: connection.DefaultGenesisState(),
}
}
// Validate performs basic genesis state validation returning an error upon any
// failure.
func (gs GenesisState) Validate() error {
return gs.ConnectionGenesis.Validate()
}
// InitGenesis initializes the ibc connection submodule's state from a provided genesis
// state.
func InitGenesis(ctx sdk.Context, k Keeper, gs GenesisState) {
connection.InitGenesis(ctx, k.ConnectionKeeper, gs.ConnectionGenesis)
}
// ExportGenesis returns the ibc connection submodule's exported genesis.
func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
return GenesisState{
ConnectionGenesis: connection.ExportGenesis(ctx, k.ConnectionKeeper),
}
}

70
x/ibc/genesis_test.go Normal file
View File

@ -0,0 +1,70 @@
package ibc
import (
"testing"
"github.com/stretchr/testify/require"
connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection"
connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
)
var (
connectionID = "connectionidone"
clientID = "clientidone"
connectionID2 = "connectionidtwo"
clientID2 = "clientidtwo"
)
func TestValidateGenesis(t *testing.T) {
testCases := []struct {
name string
genState GenesisState
expPass bool
}{
{
name: "default",
genState: DefaultGenesisState(),
expPass: true,
},
{
name: "valid genesis",
genState: GenesisState{
ConnectionGenesis: connection.NewGenesisState(
[]connection.ConnectionEnd{
connection.NewConnectionEnd(connectionexported.INIT, connectionID, clientID, connection.NewCounterparty(clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))), []string{"1.0.0"}),
},
[]connection.ConnectionPaths{
connection.NewConnectionPaths(clientID, []string{ibctypes.ConnectionPath(connectionID)}),
},
),
},
expPass: true,
},
{
name: "invalid connection genesis",
genState: GenesisState{
ConnectionGenesis: connection.NewGenesisState(
[]connection.ConnectionEnd{
connection.NewConnectionEnd(connectionexported.INIT, connectionID, "CLIENTIDONE", connection.NewCounterparty(clientID, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))), []string{"1.0.0"}),
},
nil,
),
},
expPass: false,
},
}
for _, tc := range testCases {
tc := tc
err := tc.genState.Validate()
if tc.expPass {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
}
}

View File

@ -2,6 +2,7 @@ package ibc
import (
"encoding/json"
"fmt"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
@ -56,8 +57,13 @@ func (AppModuleBasic) DefaultGenesis(_ codec.JSONMarshaler) json.RawMessage {
}
// ValidateGenesis performs genesis state validation for the ibc module.
func (AppModuleBasic) ValidateGenesis(_ codec.JSONMarshaler, _ json.RawMessage) error {
return nil
func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, bz json.RawMessage) error {
var gs GenesisState
if err := cdc.UnmarshalJSON(bz, &gs); err != nil {
return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err)
}
return gs.Validate()
}
// RegisterRESTRoutes registers the REST routes for the ibc module.
@ -120,14 +126,20 @@ func (am AppModule) NewQuerierHandler() sdk.Querier {
// InitGenesis performs genesis initialization for the ibc module. It returns
// no validator updates.
func (am AppModule) InitGenesis(_ sdk.Context, _ codec.JSONMarshaler, _ json.RawMessage) []abci.ValidatorUpdate {
func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, bz json.RawMessage) []abci.ValidatorUpdate {
var gs GenesisState
err := cdc.UnmarshalJSON(bz, &gs)
if err != nil {
panic(fmt.Sprintf("failed to unmarshal %s genesis state: %s", ModuleName, err))
}
InitGenesis(ctx, *am.keeper, gs)
return []abci.ValidatorUpdate{}
}
// ExportGenesis returns the exported genesis state as raw bytes for the ibc
// module.
func (am AppModule) ExportGenesis(_ sdk.Context, _ codec.JSONMarshaler) json.RawMessage {
return nil
func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage {
return cdc.MustMarshalJSON(ExportGenesis(ctx, *am.keeper))
}
// BeginBlock returns the begin blocker for the ibc module.