Skip to content

Commit

Permalink
feat: clientv2 module (#7936)
Browse files Browse the repository at this point in the history
* init

* move logic

* fix all tests and  types

* genesis proto and gen

* gen

* godoc

* core genesis

* core genesis wiring

* wire and test

* lint fix

* fmt

* fmt

* add defensive checks

* fix all

---------

Co-authored-by: Aditya <14364734+AdityaSripal@users.noreply.github.com>
  • Loading branch information
aljo242 and AdityaSripal authored Feb 11, 2025
1 parent ec66b11 commit 5296070
Show file tree
Hide file tree
Showing 49 changed files with 2,827 additions and 569 deletions.
19 changes: 0 additions & 19 deletions modules/core/02-client/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,25 +142,6 @@ func (k *Keeper) DeleteClientCreator(ctx context.Context, clientID string) {
store.Delete(types.CreatorKey())
}

// SetClientCounterparty sets counterpartyInfo for a given clientID
func (k *Keeper) SetClientCounterparty(ctx context.Context, clientID string, counterparty types.CounterpartyInfo) {
store := k.ClientStore(ctx, clientID)
store.Set(types.CounterpartyKey(), k.cdc.MustMarshal(&counterparty))
}

// GetClientCounterparty gets counterpartyInfo for a given clientID
func (k *Keeper) GetClientCounterparty(ctx context.Context, clientID string) (types.CounterpartyInfo, bool) {
store := k.ClientStore(ctx, clientID)
bz := store.Get(types.CounterpartyKey())
if len(bz) == 0 {
return types.CounterpartyInfo{}, false
}

var counterparty types.CounterpartyInfo
k.cdc.MustUnmarshal(bz, &counterparty)
return counterparty, true
}

// GetClientConsensusState gets the stored consensus state from a client at a given height.
func (k *Keeper) GetClientConsensusState(ctx context.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) {
store := k.ClientStore(ctx, clientID)
Expand Down
9 changes: 0 additions & 9 deletions modules/core/02-client/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,6 @@ func (suite *KeeperTestSuite) TestSetClientCreator() {
suite.Require().Equal(sdk.AccAddress(nil), getCreator)
}

func (suite *KeeperTestSuite) TestSetClientCounterparty() {
counterparty := types.NewCounterpartyInfo([][]byte{[]byte("ibc"), []byte("channel-7")}, testClientID2)
suite.keeper.SetClientCounterparty(suite.ctx, testClientID, counterparty)

retrievedCounterparty, found := suite.keeper.GetClientCounterparty(suite.ctx, testClientID)
suite.Require().True(found, "GetCounterparty failed")
suite.Require().Equal(counterparty, retrievedCounterparty, "Counterparties are not equal")
}

func (suite *KeeperTestSuite) TestSetClientConsensusState() {
suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState)

Expand Down
1 change: 0 additions & 1 deletion modules/core/02-client/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
&MsgRecoverClient{},
&MsgIBCSoftwareUpgrade{},
&MsgUpdateParams{},
&MsgRegisterCounterparty{},
)

msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
Expand Down
2 changes: 0 additions & 2 deletions modules/core/02-client/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,4 @@ var (
ErrFailedNonMembershipVerification = errorsmod.Register(SubModuleName, 31, "non-membership verification failed")
ErrRouteNotFound = errorsmod.Register(SubModuleName, 32, "light client module route not found")
ErrClientTypeNotSupported = errorsmod.Register(SubModuleName, 33, "client type not supported")
ErrInvalidCounterparty = errorsmod.Register(SubModuleName, 34, "invalid counterparty")
ErrCounterpartyNotFound = errorsmod.Register(SubModuleName, 35, "counterparty not found")
)
1 change: 0 additions & 1 deletion modules/core/02-client/types/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ func (gs GenesisState) Validate() error {
}

validClients := make(map[string]string)

for i, client := range gs.Clients {
if err := host.ClientIdentifierValidator(client.ClientId); err != nil {
return fmt.Errorf("invalid client consensus state identifier %s index %d: %w", client.ClientId, i, err)
Expand Down
8 changes: 0 additions & 8 deletions modules/core/02-client/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ const (
// KeyCreator is the key for the creator in the client-specific store
KeyCreator = "creator"

// KeyCounterparty is the key for the counterpartyInfo in the client-specific store
KeyCounterparty = "counterparty"

// AllowAllClients is the value that if set in AllowedClients param
// would allow any wired up light client modules to be allowed
AllowAllClients = "*"
Expand Down Expand Up @@ -102,8 +99,3 @@ func MustParseClientIdentifier(clientID string) string {
func CreatorKey() []byte {
return []byte(KeyCreator)
}

// CounterpartyKey returns the key under which the counterparty is stored in the client store
func CounterpartyKey() []byte {
return []byte(KeyCounterparty)
}
27 changes: 0 additions & 27 deletions modules/core/02-client/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,13 @@ var (
_ sdk.Msg = (*MsgIBCSoftwareUpgrade)(nil)
_ sdk.Msg = (*MsgRecoverClient)(nil)

_ sdk.Msg = (*MsgRegisterCounterparty)(nil)

_ sdk.HasValidateBasic = (*MsgCreateClient)(nil)
_ sdk.HasValidateBasic = (*MsgUpdateClient)(nil)
_ sdk.HasValidateBasic = (*MsgSubmitMisbehaviour)(nil)
_ sdk.HasValidateBasic = (*MsgUpgradeClient)(nil)
_ sdk.HasValidateBasic = (*MsgUpdateParams)(nil)
_ sdk.HasValidateBasic = (*MsgIBCSoftwareUpgrade)(nil)
_ sdk.HasValidateBasic = (*MsgRecoverClient)(nil)
_ sdk.HasValidateBasic = (*MsgRegisterCounterparty)(nil)

_ codectypes.UnpackInterfacesMessage = (*MsgCreateClient)(nil)
_ codectypes.UnpackInterfacesMessage = (*MsgUpdateClient)(nil)
Expand Down Expand Up @@ -321,27 +318,3 @@ func (msg *MsgUpdateParams) ValidateBasic() error {
}
return msg.Params.Validate()
}

// NewMsgRegisterCounterparty creates a new instance of MsgRegisterCounterparty.
func NewMsgRegisterCounterparty(clientID string, merklePrefix [][]byte, counterpartyClientID string, signer string) *MsgRegisterCounterparty {
return &MsgRegisterCounterparty{
ClientId: clientID,
CounterpartyMerklePrefix: merklePrefix,
CounterpartyClientId: counterpartyClientID,
Signer: signer,
}
}

// ValidateBasic performs basic checks on a MsgRegisterCounterparty.
func (msg *MsgRegisterCounterparty) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(msg.Signer); err != nil {
return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err)
}
if len(msg.CounterpartyMerklePrefix) == 0 {
return errorsmod.Wrap(ErrInvalidCounterparty, "counterparty messaging key cannot be empty")
}
if err := host.ClientIdentifierValidator(msg.ClientId); err != nil {
return err
}
return host.ClientIdentifierValidator(msg.CounterpartyClientId)
}
71 changes: 0 additions & 71 deletions modules/core/02-client/types/msgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -978,74 +978,3 @@ func TestMsgUpdateParamsGetSigners(t *testing.T) {
}
}
}

func TestMsgRegisterCounterpartyValidateBasic(t *testing.T) {
signer := ibctesting.TestAccAddress
testCases := []struct {
name string
msg *types.MsgRegisterCounterparty
expError error
}{
{
"success",
types.NewMsgRegisterCounterparty(
"testclientid",
[][]byte{[]byte("ibc"), []byte("channel-9")},
"testclientid3",
signer,
),
nil,
},
{
"failure: empty client id",
types.NewMsgRegisterCounterparty(
"",
[][]byte{[]byte("ibc"), []byte("channel-9")},
"testclientid3",
signer,
),
host.ErrInvalidID,
},
{
"failure: empty counterparty client id",
types.NewMsgRegisterCounterparty(
"testclientid",
[][]byte{[]byte("ibc"), []byte("channel-9")},
"",
signer,
),
host.ErrInvalidID,
},
{
"failure: empty counterparty messaging key",
types.NewMsgRegisterCounterparty(
"testclientid",
[][]byte{},
"testclientid3",
signer,
),
types.ErrInvalidCounterparty,
},
{
"failure: empty signer",
types.NewMsgRegisterCounterparty(
"testclientid",
[][]byte{[]byte("ibc"), []byte("channel-9")},
"testclientid3",
"badsigner",
),
ibcerrors.ErrInvalidAddress,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
err := tc.msg.ValidateBasic()
if tc.expError == nil {
require.NoError(t, err)
} else {
require.ErrorIs(t, err, tc.expError)
}
})
}
}
46 changes: 46 additions & 0 deletions modules/core/02-client/v2/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package clientv2

import (
"context"
"fmt"

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

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

// InitGenesis initializes the ibc client/v2 submodule's state from a provided genesis
// state.
func InitGenesis(ctx context.Context, k *keeper.Keeper, gs types.GenesisState) {
sdkCtx := sdk.UnwrapSDKContext(ctx)

if err := gs.Validate(); err != nil {
panic(fmt.Errorf("invalid genesis state: %w", err))
}

for _, info := range gs.CounterpartyInfos {
k.SetClientCounterparty(sdkCtx, info.ClientId, info.CounterpartyInfo)
}
}

// ExportGenesis returns the ibc client/v2 submodule's exported genesis.
func ExportGenesis(ctx context.Context, k *keeper.Keeper) types.GenesisState {
sdkCtx := sdk.UnwrapSDKContext(ctx)

clients := k.ClientV1Keeper.GetAllGenesisClients(ctx)
gs := types.GenesisState{
CounterpartyInfos: make([]types.GenesisCounterpartyInfo, 0),
}
for _, client := range clients {
counterpartyInfo, found := k.GetClientCounterparty(sdkCtx, client.ClientId)
if found {
gs.CounterpartyInfos = append(gs.CounterpartyInfos, types.GenesisCounterpartyInfo{
ClientId: client.ClientId,
CounterpartyInfo: counterpartyInfo,
})
}
}

return gs
}
54 changes: 54 additions & 0 deletions modules/core/02-client/v2/genesis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package clientv2_test

import (
clientv2 "github.com/cosmos/ibc-go/v9/modules/core/02-client/v2"
"github.com/cosmos/ibc-go/v9/modules/core/02-client/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()

path3 := ibctesting.NewPath(suite.chainB, suite.chainC)
path3.SetupV2()

app := suite.chainA.App

emptyGenesis := types.DefaultGenesisState()

// create a valid genesis state that uses the counterparty info set during setup
existingGS := clientv2.ExportGenesis(suite.chainA.GetContext(), app.GetIBCKeeper().ClientV2Keeper)

tests := []struct {
name string
genState types.GenesisState
expectedState types.GenesisState
}{
{
name: "no modifications genesis",
genState: emptyGenesis,
expectedState: existingGS,
},
{
name: "valid - default genesis",
genState: types.DefaultGenesisState(),
expectedState: existingGS,
},
}

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

clientv2.InitGenesis(suite.chainA.GetContext(), clientV2Keeper, tt.genState)

exported := clientv2.ExportGenesis(suite.chainA.GetContext(), clientV2Keeper)
suite.Require().Equal(tt.expectedState, exported)
})
}
}
49 changes: 49 additions & 0 deletions modules/core/02-client/v2/keeper/grpc_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package keeper

import (
"context"
"fmt"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

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

"github.com/cosmos/ibc-go/v9/modules/core/02-client/v2/types"
host "github.com/cosmos/ibc-go/v9/modules/core/24-host"
)

var _ types.QueryServer = (*queryServer)(nil)

// queryServer implements the 02-client/v2 types.QueryServer interface.
// It embeds the client keeper to leverage store access while limiting the api of the client keeper.
type queryServer struct {
*Keeper
}

// NewQueryServer returns a new 02-client/v2 types.QueryServer implementation.
func NewQueryServer(k *Keeper) types.QueryServer {
return &queryServer{
Keeper: k,
}
}

// CounterpartyInfo gets the CounterpartyInfo from the store corresponding to the request client ID.
func (q queryServer) CounterpartyInfo(ctx context.Context, req *types.QueryCounterpartyInfoRequest) (*types.QueryCounterpartyInfoResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

if err := host.ClientIdentifierValidator(req.ClientId); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

sdkCtx := sdk.UnwrapSDKContext(ctx)

info, found := q.GetClientCounterparty(sdkCtx, req.ClientId)
if !found {
return nil, status.Error(codes.NotFound, fmt.Sprintf("client %s counterparty not found", req.ClientId))
}

return &types.QueryCounterpartyInfoResponse{CounterpartyInfo: &info}, nil
}
Loading

0 comments on commit 5296070

Please sign in to comment.