Skip to content

Commit

Permalink
feat: multi-ibc-relayer supported (#205)
Browse files Browse the repository at this point in the history
* feat: multi-relayer supported

* fix: reflect comment

* fix:name

* fix: slice module

* fix: remove add permissioned relayers & types.proto

* fix: convert function & unused test code

* fix: init genesis & add some test code
  • Loading branch information
djm07073 authored Jun 21, 2024
1 parent d8aa158 commit 9af2a79
Show file tree
Hide file tree
Showing 26 changed files with 874 additions and 527 deletions.
12 changes: 6 additions & 6 deletions app/hook/bridge_hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ type ChannelKeeper interface {

type PermKeeper interface {
HasPermission(ctx context.Context, portID, channelID string, relayer sdk.AccAddress) (bool, error)
SetPermissionedRelayer(ctx context.Context, portID, channelID string, relayer sdk.AccAddress) error
GetPermissionedRelayer(ctx context.Context, portID, channelID string) (sdk.AccAddress, error)
SetPermissionedRelayers(ctx context.Context, portID, channelID string, relayers []sdk.AccAddress) error
GetPermissionedRelayers(ctx context.Context, portID, channelID string) ([]sdk.AccAddress, error)
}

func NewBridgeHook(channelKeeper ChannelKeeper, permKeeper PermKeeper, ac address.Codec) BridgeHook {
Expand Down Expand Up @@ -61,14 +61,14 @@ func (h BridgeHook) BridgeCreated(
}

// check if the channel has a permissioned relayer
if _, err := h.IBCPermKeeper.GetPermissionedRelayer(ctx, portID, channelID); err == nil {
if _, err := h.IBCPermKeeper.GetPermissionedRelayers(ctx, portID, channelID); err == nil {
return permtypes.ErrAlreadyTaken.Wrap("failed to claim permissioned relayer")
} else if !errors.Is(err, collections.ErrNotFound) {
return err
}

// register challenger as channel relayer
if err = h.IBCPermKeeper.SetPermissionedRelayer(sdkCtx, portID, channelID, challenger); err != nil {
if err = h.IBCPermKeeper.SetPermissionedRelayers(sdkCtx, portID, channelID, []sdk.AccAddress{challenger}); err != nil {
return err
}
}
Expand Down Expand Up @@ -96,7 +96,7 @@ func (h BridgeHook) BridgeChallengerUpdated(
portID, channelID := permChannel.PortID, permChannel.ChannelID

// update relayer to a new challenger
if err = h.IBCPermKeeper.SetPermissionedRelayer(sdkCtx, portID, channelID, challenger); err != nil {
if err = h.IBCPermKeeper.SetPermissionedRelayers(sdkCtx, portID, channelID, []sdk.AccAddress{challenger}); err != nil {
return err
}
}
Expand Down Expand Up @@ -154,7 +154,7 @@ func (h BridgeHook) BridgeMetadataUpdated(
}

// register challenger as channel relayer
if err = h.IBCPermKeeper.SetPermissionedRelayer(sdkCtx, portID, channelID, challenger); err != nil {
if err = h.IBCPermKeeper.SetPermissionedRelayers(sdkCtx, portID, channelID, []sdk.AccAddress{challenger}); err != nil {
return err
}
}
Expand Down
26 changes: 19 additions & 7 deletions app/hook/bridge_hook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,37 @@ func (k MockChannelKeeper) GetNextSequenceSend(ctx sdk.Context, portID, channelI
return seq, ok
}

type MockPermRelayerList struct {
Relayers []sdk.AccAddress
}

func (l MockPermRelayerList) Equals(relayer sdk.AccAddress) bool {
for _, r := range l.Relayers {
if r.Equals(relayer) {
return true
}
}
return false
}

type MockPermKeeper struct {
perms map[string]sdk.AccAddress
perms map[string]MockPermRelayerList
}

func (k MockPermKeeper) HasPermission(ctx context.Context, portID, channelID string, relayer sdk.AccAddress) (bool, error) {
return k.perms[portID+"/"+channelID].Equals(relayer), nil
}

func (k MockPermKeeper) SetPermissionedRelayer(ctx context.Context, portID, channelID string, relayer sdk.AccAddress) error {
k.perms[portID+"/"+channelID] = relayer
func (k MockPermKeeper) SetPermissionedRelayers(ctx context.Context, portID, channelID string, relayers []sdk.AccAddress) error {
k.perms[portID+"/"+channelID] = MockPermRelayerList{Relayers: relayers}
return nil
}

func (k MockPermKeeper) GetPermissionedRelayer(ctx context.Context, portID, channelID string) (sdk.AccAddress, error) {
func (k MockPermKeeper) GetPermissionedRelayers(ctx context.Context, portID, channelID string) ([]sdk.AccAddress, error) {
if _, ok := k.perms[portID+"/"+channelID]; !ok {
return nil, collections.ErrNotFound
}

return k.perms[portID+"/"+channelID], nil
return k.perms[portID+"/"+channelID].Relayers, nil
}

func setup() (context.Context, hook.BridgeHook) {
Expand All @@ -63,7 +75,7 @@ func setup() (context.Context, hook.BridgeHook) {
"transfer/channel-2": 1,
},
}, MockPermKeeper{
perms: make(map[string]sdk.AccAddress),
perms: make(map[string]MockPermRelayerList),
}, address.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()))

ms := store.NewCommitMultiStore(dbm.NewMemDB(), log.NewNopLogger(), metrics.NewNoOpMetrics())
Expand Down
2 changes: 1 addition & 1 deletion proto/ibc/applications/perm/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ option go_package = "github.com/initia-labs/initia/x/ibc/perm/types";

// GenesisState defines the ibc perm genesis state
message GenesisState {
repeated PermissionedRelayer permissioned_relayers = 1
repeated PermissionedRelayers permissioned_relayers = 1
[(gogoproto.moretags) = "yaml:\"permissioned_relayers\"", (gogoproto.nullable) = false];
}
26 changes: 13 additions & 13 deletions proto/ibc/applications/perm/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,43 @@ option go_package = "github.com/initia-labs/initia/x/ibc/perm/types";

// Query provides defines the gRPC querier service.
service Query {
// PermissionedRelayer queries a permissioned ibc relayer for the specific channel.
rpc PermissionedRelayer(QueryPermissionedRelayerRequest) returns (QueryPermissionedRelayerResponse) {
// PermissionedRelayersByChannel queries a set of permissioned ibc relayers for the specific channel.
rpc PermissionedRelayersByChannel(QueryPermissionedRelayersByChannelRequest) returns (QueryPermissionedRelayersByChannelResponse) {
option (google.api.http).get = "/ibc/apps/perm/v1/relayers/{port_id}/{channel_id}";
}

// PermissionedRelayers queries a permissioned ibc relayers.
rpc PermissionedRelayers(QueryPermissionedRelayersRequest) returns (QueryPermissionedRelayersResponse) {
// AllPermissionedRelayers queries all sets of permissioned relayers for all channels.
rpc AllPermissionedRelayers(QueryAllPermissionedRelayersRequest) returns (QueryAllPermissionedRelayersResponse) {
option (google.api.http).get = "/ibc/apps/perm/v1/relayers";
}
}

// QueryPermissionedRelayerRequest is the request type for the Query/PermissionedRelayer RPC
// QueryPermissionedRelayersOfOneChannelRequest is the request type for the Query/PermissionedRelayer RPC
// method
message QueryPermissionedRelayerRequest {
message QueryPermissionedRelayersByChannelRequest {
string port_id = 1;
string channel_id = 2;
}

// QueryPermissionedRelayerResponse is the response type for the Query/PermissionedRelayer RPC
// method.
message QueryPermissionedRelayerResponse {
message QueryPermissionedRelayersByChannelResponse {
// class_trace returns the requested class id trace information.
PermissionedRelayer permissioned_relayer = 1;
PermissionedRelayers permissioned_relayers = 1;
}

// QueryPermissionedRelayersRequest is the request type for the Query/PermissionedRelayers RPC
// QueryAllPermissionedRelayersRequest is the request type for the Query/AllPermissionedRelayers RPC
// method
message QueryPermissionedRelayersRequest {
message QueryAllPermissionedRelayersRequest {
// pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}

// QueryPermissionedRelayersResponse is the response type for the Query/PermissionedRelayers RPC
// QueryAllPermissionedRelayersResponse is the response type for the Query/AllPermissionedRelayers RPC
// method.
message QueryPermissionedRelayersResponse {
message QueryAllPermissionedRelayersResponse {
// class_trace returns the requested class id trace information.
repeated PermissionedRelayer permissioned_relayers = 1 [(gogoproto.nullable) = false];
repeated PermissionedRelayers permissioned_relayers = 1 [(gogoproto.nullable) = false];
// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
23 changes: 14 additions & 9 deletions proto/ibc/applications/perm/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@ import "amino/amino.proto";
import "cosmos/msg/v1/msg.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";

import "ibc/applications/perm/v1/types.proto";
option go_package = "github.com/initia-labs/initia/x/ibc/perm/types";

// Msg defines the ibc/perm Msg service
service Msg {
option (cosmos.msg.v1.service) = true;

// SetPermissionedRelayer defines a rpc handler method for MsgSetPermissionedRelayer.
rpc SetPermissionedRelayer(MsgSetPermissionedRelayer) returns (MsgSetPermissionedRelayerResponse);
// SetPermissionedRelayers defines a rpc handler method for MsgSetPermissionedRelayers.
rpc SetPermissionedRelayers(MsgSetPermissionedRelayers) returns (MsgSetPermissionedRelayersResponse);

}

// MsgSetPermissionedRelayer defines msg to set permissioned relyer for
// MsgSetPermissionedRelayers defines msg to set permissioned relyer for
// the specific ibc channel.
message MsgSetPermissionedRelayer {
message MsgSetPermissionedRelayers {
option (cosmos.msg.v1.signer) = "authority";
option (amino.name) = "perm/MsgSetPermissionedRelayer";
option (amino.name) = "perm/MsgSetPermissionedRelayers";

option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
Expand All @@ -30,8 +31,12 @@ message MsgSetPermissionedRelayer {
string authority = 1 [(gogoproto.moretags) = "yaml:\"authority\"", (cosmos_proto.scalar) = "cosmos.AddressString"];
string port_id = 2 [(gogoproto.moretags) = "yaml:\"port_id\""];
string channel_id = 3 [(gogoproto.moretags) = "yaml:\"channel_id\""];
string relayer = 4 [(gogoproto.moretags) = "yaml:\"relayer\""];
repeated string relayers = 4 [(gogoproto.moretags) = "yaml:\"relayers\""];
}

// MsgSetPermissionedRelayerResponse defines the Msg/SetPermissionedRelayer response type.
message MsgSetPermissionedRelayerResponse {}
// MsgSetPermissionedRelayersResponse defines the Msg/SetPermissionedRelayer response type.
message MsgSetPermissionedRelayersResponse {}




8 changes: 6 additions & 2 deletions proto/ibc/applications/perm/v1/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ option go_package = "github.com/initia-labs/initia/x/ibc/perm/types";

// PermissionedRelayer is used to specifiy the permissioned relayer for
// the specific port-id:channel-id pair.
message PermissionedRelayer {
message PermissionedRelayers {
string port_id = 1;
string channel_id = 2;
string relayer = 3;
repeated string relayers = 3;
}

message PermissionedRelayersList {
repeated string relayers = 1;
}
3 changes: 1 addition & 2 deletions x/ibc/perm/ibc_module.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package perm

import (
"bytes"
"errors"
"fmt"

Expand Down Expand Up @@ -160,7 +159,7 @@ func (im IBCMiddleware) OnRecvPacket(
) ibcexported.Acknowledgement {
if permissionedRelayer, err := im.keeper.PermissionedRelayers.Get(ctx, collections.Join(packet.DestinationPort, packet.DestinationChannel)); err != nil && !errors.Is(err, collections.ErrNotFound) {
return newEmitErrorAcknowledgement(ctx, err)
} else if err == nil && !bytes.Equal(relayer, permissionedRelayer) {
} else if err == nil && !permissionedRelayer.HasRelayer(relayer.String()) {
/*
Raise a panic to prevent abnormal relayers from disrupting operations by
continuously sending error acknowledgements. For instance, abnormal relaying
Expand Down
26 changes: 16 additions & 10 deletions x/ibc/perm/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,36 @@ import (

// InitGenesis initializes the ibc-perm state.
func (k Keeper) InitGenesis(ctx sdk.Context, genesisState types.GenesisState) {
for _, channelRelayer := range genesisState.PermissionedRelayers {
addr, err := k.ac.StringToBytes(channelRelayer.Relayer)
if err != nil {
panic(err)
}

if err := k.PermissionedRelayers.Set(ctx, collections.Join(channelRelayer.PortId, channelRelayer.ChannelId), addr); err != nil {
for _, relayersByChannel := range genesisState.PermissionedRelayers {
var channelRelayers []string
for _, channelRelayer := range relayersByChannel.Relayers {
_, err := k.ac.StringToBytes(channelRelayer)
if err != nil {
panic(err)
}
channelRelayers = append(channelRelayers, channelRelayer)
}
if err := k.PermissionedRelayers.Set(ctx, collections.Join(relayersByChannel.PortId, relayersByChannel.ChannelId), types.PermissionedRelayersList{
Relayers: channelRelayers,
}); err != nil {
panic(err)
}
}
}

// ExportGenesis exports ibc-perm module's channel relayers.
func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
channelRelayers := []types.PermissionedRelayer{}
err := k.IteratePermissionedRelayers(ctx, func(channelRelayer types.PermissionedRelayer) (bool, error) {
channelRelayers = append(channelRelayers, channelRelayer)
channelRelayerSets := []types.PermissionedRelayers{}
err := k.IteratePermissionedRelayers(ctx, func(channelRelayer types.PermissionedRelayers) (bool, error) {
channelRelayerSets = append(channelRelayerSets, channelRelayer)
return false, nil
})
if err != nil {
panic(err)
}

return &types.GenesisState{
PermissionedRelayers: channelRelayers,
PermissionedRelayers: channelRelayerSets,
}
}
43 changes: 39 additions & 4 deletions x/ibc/perm/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,42 @@ import (
"github.com/initia-labs/initia/x/ibc/perm/types"
)

func Test_InitGenesis(t *testing.T) {
ctx, k := _createTestInput(t, dbm.NewMemDB())
channelA := "channel-123"
channelB := "channel-456"
portA := "port-123"
portB := "port-456"

pubKeyA := secp256k1.GenPrivKey().PubKey()
pubKeyB := secp256k1.GenPrivKey().PubKey()

addrA := sdk.AccAddress(pubKeyA.Address())
addrB := sdk.AccAddress(pubKeyB.Address())

k.InitGenesis(ctx, types.GenesisState{
PermissionedRelayers: []types.PermissionedRelayers{
{
PortId: portA,
ChannelId: channelA,
Relayers: []string{addrA.String()},
},
{
PortId: portB,
ChannelId: channelB,
Relayers: []string{addrB.String()},
},
},
})

ok, err := k.HasPermission(ctx, portA, channelA, addrA)
require.NoError(t, err)
require.True(t, ok)

ok, _ = k.HasPermission(ctx, portB, channelB, addrB)
require.NoError(t, err)
require.True(t, ok)
}
func Test_ExportGenesis(t *testing.T) {
ctx, k := _createTestInput(t, dbm.NewMemDB())

Expand All @@ -27,21 +63,20 @@ func Test_ExportGenesis(t *testing.T) {
addrB := sdk.AccAddress(pubKeyB.Address())

genState := types.NewGenesisState(
[]types.PermissionedRelayer{
[]types.PermissionedRelayers{
{
PortId: portA,
ChannelId: channelA,
Relayer: addrA.String(),
Relayers: []string{addrA.String()},
},
{
PortId: portB,
ChannelId: channelB,
Relayer: addrB.String(),
Relayers: []string{addrB.String()},
},
},
)
k.InitGenesis(ctx, *genState)

exportedState := k.ExportGenesis(ctx)
require.Equal(t, genState, exportedState)
}
Loading

0 comments on commit 9af2a79

Please sign in to comment.