Skip to content
1 change: 1 addition & 0 deletions app/tags
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,4 @@ v6.0.5
v6.0.6
v6.1.0
v6.1.4
v6.4.0
13 changes: 13 additions & 0 deletions app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,19 @@ func (app *App) RegisterUpgradeHandlers() {
return newVM, err
}

// IBC Toggle migration: initialize InboundEnabled and OutboundEnabled params
if upgradeName == "v6.4.0" {
newVM, err := app.mm.RunMigrations(ctx, app.configurator, fromVM)
if err != nil {
return newVM, err
}

// Enable IBC inbound and outbound by default
app.IBCKeeper.SetInboundEnabled(ctx, true)
app.IBCKeeper.SetOutboundEnabled(ctx, true)
return newVM, err
}

return app.mm.RunMigrations(ctx, app.configurator, fromVM)
})
}
Expand Down
8 changes: 8 additions & 0 deletions sei-ibc-go/modules/core/02-client/keeper/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@ import (
"github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/exported"
)

// ErrInboundDisabled is the error for when inbound is disabled
var ErrInboundDisabled = sdkerrors.Register("ibc-client", 101, "ibc inbound disabled")

// CreateClient creates a new client state and populates it with a given consensus
// state as defined in https://github.com/cosmos/ibc/tree/master/spec/core/ics-002-client-semantics#create
func (k Keeper) CreateClient(
ctx sdk.Context, clientState exported.ClientState, consensusState exported.ConsensusState,
) (string, error) {
// inbound gating: disallow client creation as part of inbound handshakes when inbound disabled
if !k.IsInboundEnabled(ctx) {
return "", sdkerrors.Wrap(ErrInboundDisabled, "client creation inbound disabled")
}

params := k.GetParams(ctx)
if !params.IsAllowedClient(clientState.ClientType()) {
return "", sdkerrors.Wrapf(
Expand Down
21 changes: 21 additions & 0 deletions sei-ibc-go/modules/core/02-client/keeper/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ func (suite *KeeperTestSuite) TestCreateClient() {
}
}

// TestCreateClient_BlockedWhenInboundDisabled tests that CreateClient
// is blocked when inbound IBC is disabled.
func (suite *KeeperTestSuite) TestCreateClient_BlockedWhenInboundDisabled() {
// disable inbound on chainA
ibcKeeperA := suite.chainA.App.GetIBCKeeper()
ibcKeeperA.SetInboundEnabled(suite.chainA.GetContext(), false)
suite.Require().False(ibcKeeperA.IsInboundEnabled(suite.chainA.GetContext()))

clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false)

// attempt CreateClient with inbound disabled
clientID, err := suite.chainA.App.GetIBCKeeper().ClientKeeper.CreateClient(
suite.chainA.GetContext(), clientState, suite.consensusState,
)

// should fail with ErrInboundDisabled
suite.Require().Error(err)
suite.Require().Contains(err.Error(), "inbound")
suite.Require().Equal("", clientID)
}

func (suite *KeeperTestSuite) TestUpdateClientTendermint() {
var (
path *ibctesting.Path
Expand Down
10 changes: 10 additions & 0 deletions sei-ibc-go/modules/core/02-client/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import (
ibctmtypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/light-clients/07-tendermint/types"
)

// KeyInboundEnabled is the param key for inbound enabled
var KeyInboundEnabled = []byte("InboundEnabled")

// Keeper represents a type that grants read and write permissions to any client
// state information
type Keeper struct {
Expand Down Expand Up @@ -53,6 +56,13 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName)
}

// IsInboundEnabled returns true if inbound IBC is enabled.
func (k Keeper) IsInboundEnabled(ctx sdk.Context) bool {
var inbound bool
k.paramSpace.Get(ctx, KeyInboundEnabled, &inbound)
return inbound
}

// GenerateClientIdentifier returns the next client identifier.
func (k Keeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string {
nextClientSeq := k.GetNextClientSequence(ctx)
Expand Down
10 changes: 10 additions & 0 deletions sei-ibc-go/modules/core/03-connection/keeper/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ func (k Keeper) ConnOpenInit(
version *types.Version,
delayPeriod uint64,
) (string, error) {
// outbound gating: disallow outbound connection inits when outbound disabled
if !k.IsOutboundEnabled(ctx) {
return "", sdkerrors.Wrap(ErrOutboundDisabled, "connection outbound disabled")
}

versions := types.GetCompatibleVersions()
if version != nil {
if !types.IsSupportedVersion(version) {
Expand Down Expand Up @@ -75,6 +80,11 @@ func (k Keeper) ConnOpenTry(
proofHeight exported.Height, // height at which relayer constructs proof of A storing connectionEnd in state
consensusHeight exported.Height, // latest height of chain B which chain A has stored in its chain B client
) (string, error) {
// inbound gating: disallow inbound connection tries when inbound disabled
if !k.IsInboundEnabled(ctx) {
return "", sdkerrors.Wrap(ErrInboundDisabled, "connection inbound disabled")
}

var (
connectionID string
previousConnection types.ConnectionEnd
Expand Down
73 changes: 73 additions & 0 deletions sei-ibc-go/modules/core/03-connection/keeper/handshake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,79 @@ func (suite *KeeperTestSuite) TestConnOpenAck() {
}
}

// TestConnOpenTry_BlockedWhenInboundDisabled tests that ConnOpenTry
// is blocked when inbound IBC is disabled.
func (suite *KeeperTestSuite) TestConnOpenTry_BlockedWhenInboundDisabled() {
suite.SetupTest()

path := ibctesting.NewPath(suite.chainA, suite.chainB)
suite.coordinator.SetupClients(path)

// chainA initiates connection
err := path.EndpointA.ConnOpenInit()
suite.Require().NoError(err)

// retrieve client state of chainA to pass as counterpartyClient
counterpartyClient := suite.chainA.GetClientState(path.EndpointA.ClientID)

// disable inbound on chainB
ibcKeeperB := suite.chainB.App.GetIBCKeeper()
ibcKeeperB.SetInboundEnabled(suite.chainB.GetContext(), false)
suite.Require().False(ibcKeeperB.IsInboundEnabled(suite.chainB.GetContext()))

// ensure client is up to date on chainB
err = path.EndpointB.UpdateClient()
suite.Require().NoError(err)

counterparty := types.NewCounterparty(path.EndpointA.ClientID, path.EndpointA.ConnectionID, suite.chainA.GetPrefix())

connectionKey := host.ConnectionKey(path.EndpointA.ConnectionID)
proofInit, proofHeight := suite.chainA.QueryProof(connectionKey)

consensusHeight := counterpartyClient.GetLatestHeight()
consensusKey := host.FullConsensusStateKey(path.EndpointA.ClientID, consensusHeight)
proofConsensus, _ := suite.chainA.QueryProof(consensusKey)

clientKey := host.FullClientStateKey(path.EndpointA.ClientID)
proofClient, _ := suite.chainA.QueryProof(clientKey)

// attempt ConnOpenTry on chainB with inbound disabled
_, err = suite.chainB.App.GetIBCKeeper().ConnectionKeeper.ConnOpenTry(
suite.chainB.GetContext(), "", counterparty, 0, path.EndpointB.ClientID, counterpartyClient,
types.GetCompatibleVersions(), proofInit, proofClient, proofConsensus,
proofHeight, consensusHeight,
)

// should fail with ErrInboundDisabled
suite.Require().Error(err)
suite.Require().Contains(err.Error(), "inbound")
}

// TestConnOpenInit_BlockedWhenOutboundDisabled tests that ConnOpenInit
// is blocked when outbound IBC is disabled.
func (suite *KeeperTestSuite) TestConnOpenInit_BlockedWhenOutboundDisabled() {
suite.SetupTest()

path := ibctesting.NewPath(suite.chainA, suite.chainB)
suite.coordinator.SetupClients(path)

// disable outbound on chainA
ibcKeeperA := suite.chainA.App.GetIBCKeeper()
ibcKeeperA.SetOutboundEnabled(suite.chainA.GetContext(), false)
suite.Require().False(ibcKeeperA.IsOutboundEnabled(suite.chainA.GetContext()))

counterparty := types.NewCounterparty(path.EndpointB.ClientID, "", suite.chainB.GetPrefix())

// attempt ConnOpenInit on chainA with outbound disabled
_, err := suite.chainA.App.GetIBCKeeper().ConnectionKeeper.ConnOpenInit(
suite.chainA.GetContext(), path.EndpointA.ClientID, counterparty, types.DefaultIBCVersion, 0,
)

// should fail with ErrOutboundDisabled
suite.Require().Error(err)
suite.Require().Contains(err.Error(), "outbound")
}

// TestConnOpenConfirm - chainB calls ConnOpenConfirm to confirm that
// chainA state is now OPEN.
func (suite *KeeperTestSuite) TestConnOpenConfirm() {
Expand Down
26 changes: 26 additions & 0 deletions sei-ibc-go/modules/core/03-connection/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ import (
"github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/exported"
)

// KeyInboundEnabled is the param key for inbound enabled
var KeyInboundEnabled = []byte("InboundEnabled")

// KeyOutboundEnabled is the param key for outbound enabled
var KeyOutboundEnabled = []byte("OutboundEnabled")

// ErrInboundDisabled is the error for when inbound is disabled
var ErrInboundDisabled = sdkerrors.Register("ibc-connection", 101, "ibc inbound disabled")

// ErrOutboundDisabled is the error for when outbound is disabled
var ErrOutboundDisabled = sdkerrors.Register("ibc-connection", 102, "ibc outbound disabled")

// Keeper defines the IBC connection keeper
type Keeper struct {
// implements gRPC QueryServer interface
Expand Down Expand Up @@ -46,6 +58,20 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName)
}

// IsInboundEnabled returns true if inbound IBC is enabled.
func (k Keeper) IsInboundEnabled(ctx sdk.Context) bool {
var inbound bool
k.paramSpace.Get(ctx, KeyInboundEnabled, &inbound)
return inbound
}

// IsOutboundEnabled returns true if outbound IBC is enabled.
func (k Keeper) IsOutboundEnabled(ctx sdk.Context) bool {
var outbound bool
k.paramSpace.Get(ctx, KeyOutboundEnabled, &outbound)
return outbound
}

// GetCommitmentPrefix returns the IBC connection store prefix as a commitment
// Prefix
func (k Keeper) GetCommitmentPrefix() exported.Prefix {
Expand Down
16 changes: 16 additions & 0 deletions sei-ibc-go/modules/core/04-channel/keeper/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ import (
"github.com/sei-protocol/sei-chain/sei-ibc-go/modules/core/exported"
)

// ErrInboundDisabledHandshake is the error for when inbound is disabled during channel handshake
var ErrInboundDisabledHandshake = sdkerrors.Register("ibc-channel-handshake", 101, "ibc inbound disabled")

// ErrOutboundDisabledHandshake is the error for when outbound is disabled during channel handshake
var ErrOutboundDisabledHandshake = sdkerrors.Register("ibc-channel-handshake", 102, "ibc outbound disabled")

// ChanOpenInit is called by a module to initiate a channel opening handshake with
// a module on another chain. The counterparty channel identifier is validated to be
// empty in msg validation.
Expand All @@ -27,6 +33,11 @@ func (k Keeper) ChanOpenInit(
counterparty types.Counterparty,
version string,
) (string, *capabilitytypes.Capability, error) {
// outbound gating: disallow outbound channel inits when outbound disabled
if !k.IsOutboundEnabled(ctx) {
return "", nil, sdkerrors.Wrap(ErrOutboundDisabledHandshake, "channel outbound disabled")
}

// connection hop length checked on msg.ValidateBasic()
connectionEnd, found := k.connectionKeeper.GetConnection(ctx, connectionHops[0])
if !found {
Expand Down Expand Up @@ -106,6 +117,11 @@ func (k Keeper) ChanOpenTry(
proofInit []byte,
proofHeight exported.Height,
) (string, *capabilitytypes.Capability, error) {
// inbound gating: disallow inbound channel tries when inbound disabled
if !k.IsInboundEnabled(ctx) {
return "", nil, sdkerrors.Wrap(ErrInboundDisabledHandshake, "channel inbound disabled")
}

var (
previousChannel types.Channel
previousChannelFound bool
Expand Down
74 changes: 74 additions & 0 deletions sei-ibc-go/modules/core/04-channel/keeper/handshake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,80 @@ func (suite *KeeperTestSuite) TestChanOpenTry() {
}
}

// TestChanOpenTry_BlockedWhenInboundDisabled tests that ChanOpenTry
// is blocked when inbound IBC is disabled.
func (suite *KeeperTestSuite) TestChanOpenTry_BlockedWhenInboundDisabled() {
suite.SetupTest()

path := ibctesting.NewPath(suite.chainA, suite.chainB)
suite.coordinator.SetupConnections(path)
path.SetChannelOrdered()

// chainA opens channel
err := path.EndpointA.ChanOpenInit()
suite.Require().NoError(err)

// create port capability on chainB
suite.chainB.CreatePortCapability(suite.chainB.GetSimApp().ScopedIBCMockKeeper, ibctesting.MockPort)
portCap := suite.chainB.GetPortCapability(ibctesting.MockPort)

// disable inbound on chainB
ibcKeeperB := suite.chainB.App.GetIBCKeeper()
ibcKeeperB.SetInboundEnabled(suite.chainB.GetContext(), false)
suite.Require().False(ibcKeeperB.IsInboundEnabled(suite.chainB.GetContext()))

// ensure client is up to date on chainB
err = path.EndpointB.UpdateClient()
suite.Require().NoError(err)

counterparty := types.NewCounterparty(path.EndpointB.ChannelConfig.PortID, ibctesting.FirstChannelID)

channelKey := host.ChannelKey(counterparty.PortId, counterparty.ChannelId)
proof, proofHeight := suite.chainA.QueryProof(channelKey)

// attempt ChanOpenTry on chainB with inbound disabled
_, _, err = suite.chainB.App.GetIBCKeeper().ChannelKeeper.ChanOpenTry(
suite.chainB.GetContext(), types.ORDERED, []string{path.EndpointB.ConnectionID},
path.EndpointB.ChannelConfig.PortID, "", portCap, counterparty, path.EndpointA.ChannelConfig.Version,
proof, proofHeight,
)

// should fail with ErrInboundDisabled
suite.Require().Error(err)
suite.Require().Contains(err.Error(), "inbound")
}

// TestChanOpenInit_BlockedWhenOutboundDisabled tests that ChanOpenInit
// is blocked when outbound IBC is disabled.
func (suite *KeeperTestSuite) TestChanOpenInit_BlockedWhenOutboundDisabled() {
suite.SetupTest()

path := ibctesting.NewPath(suite.chainA, suite.chainB)
suite.coordinator.SetupConnections(path)
path.SetChannelOrdered()

// create port capability on chainA
suite.chainA.CreatePortCapability(suite.chainA.GetSimApp().ScopedIBCMockKeeper, ibctesting.MockPort)
portCap := suite.chainA.GetPortCapability(ibctesting.MockPort)

// disable outbound on chainA
ibcKeeperA := suite.chainA.App.GetIBCKeeper()
ibcKeeperA.SetOutboundEnabled(suite.chainA.GetContext(), false)
suite.Require().False(ibcKeeperA.IsOutboundEnabled(suite.chainA.GetContext()))

counterparty := types.NewCounterparty(ibctesting.MockPort, "")

// attempt ChanOpenInit on chainA with outbound disabled
_, _, err := suite.chainA.App.GetIBCKeeper().ChannelKeeper.ChanOpenInit(
suite.chainA.GetContext(), types.ORDERED, []string{path.EndpointA.ConnectionID},
path.EndpointA.ChannelConfig.PortID, portCap, counterparty, path.EndpointA.ChannelConfig.Version,
)

// should fail with ErrOutboundDisabled
suite.Require().Error(err)
suite.Require().Contains(err.Error(), "outbound")
}

// TestChanOpenAck tests the OpenAck handshake call for channels. It uses message passing
// to enter into the appropriate state and then calls ChanOpenAck directly. The handshake
// call is occurring on chainA.
Expand Down
Loading
Loading