Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICS5: Integrate Dynamic Capabilities into IBC #5872

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,16 +176,19 @@ func NewSimApp(
app.subspaces[crisis.ModuleName] = app.ParamsKeeper.Subspace(crisis.DefaultParamspace)
app.subspaces[evidence.ModuleName] = app.ParamsKeeper.Subspace(evidence.DefaultParamspace)

// add capability keeper and ScopeToModule for ibc module
app.CapabilityKeeper = capability.NewKeeper(
app.cdc, keys[capability.StoreKey], memKeys[capability.MemStoreKey],
)
scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibc.ModuleName)

// add keepers
app.AccountKeeper = auth.NewAccountKeeper(
appCodec, keys[auth.StoreKey], app.subspaces[auth.ModuleName], auth.ProtoBaseAccount,
)
app.BankKeeper = bank.NewBaseKeeper(
appCodec, keys[bank.StoreKey], app.AccountKeeper, app.subspaces[bank.ModuleName], app.BlacklistedAccAddrs(),
)
app.CapabilityKeeper = capability.NewKeeper(
app.cdc, keys[capability.StoreKey], memKeys[capability.MemStoreKey],
)
app.SupplyKeeper = supply.NewKeeper(
appCodec, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms,
)
Expand Down Expand Up @@ -236,7 +239,7 @@ func NewSimApp(
)

app.IBCKeeper = ibc.NewKeeper(
app.cdc, keys[ibc.StoreKey], app.StakingKeeper,
app.cdc, keys[ibc.StoreKey], app.StakingKeeper, scopedIBCKeeper,
)

transferCapKey := app.IBCKeeper.PortKeeper.BindPort(bank.ModuleName)
Expand Down
16 changes: 8 additions & 8 deletions x/ibc/04-channel/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import (

// HandleMsgChannelOpenInit defines the sdk.Handler for MsgChannelOpenInit
func HandleMsgChannelOpenInit(ctx sdk.Context, k keeper.Keeper, msg types.MsgChannelOpenInit) (*sdk.Result, error) {
err := k.ChanOpenInit(
_, err := k.ChanOpenInit(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what to do with the returned capability here. I see how a module can call the keeper's ChanOpenInit method, claim the returned capability and continue the handshake from there.

However, I don't see how to return the capability to an end user since it's only the same capability if the end user somehow gets the same reference passed to it. For example, I don't see how this would work at all in the current relayer flow since the relayer can't be given the channel capability here and then pass it to the next ChanOpenAck msg

Seems like enforcing capabilities will disallow bare creation of channels? Instead all channel creation would have to be mediated through the modules themselves which can bind ports and then start the channel handshake amongst themselves with users prompting the modules to move forward on the handshake

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like enforcing capabilities will disallow bare creation of channels? Instead all channel creation would have to be mediated through the modules themselves which can bind ports and then start the channel handshake amongst themselves with users prompting the modules to move forward on the handshake

This capability should be passed back to the module which has bound to the port - the module can then call ClaimCapability and use the capability to continue the handshake & send packets later on. The relayer can send the actual transactions; that's fine - the module just needs to claim the capability and fetch it (under some new name) when required.

ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortID, msg.ChannelID,
msg.Channel.Counterparty, msg.Channel.Version,
msg.PortCap, msg.Channel.Counterparty, msg.Channel.Version,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -39,8 +39,8 @@ func HandleMsgChannelOpenInit(ctx sdk.Context, k keeper.Keeper, msg types.MsgCha

// HandleMsgChannelOpenTry defines the sdk.Handler for MsgChannelOpenTry
func HandleMsgChannelOpenTry(ctx sdk.Context, k keeper.Keeper, msg types.MsgChannelOpenTry) (*sdk.Result, error) {
err := k.ChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortID, msg.ChannelID,
msg.Channel.Counterparty, msg.Channel.Version, msg.CounterpartyVersion, msg.ProofInit, msg.ProofHeight,
_, err := k.ChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortID, msg.ChannelID,
msg.PortCap, msg.Channel.Counterparty, msg.Channel.Version, msg.CounterpartyVersion, msg.ProofInit, msg.ProofHeight,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -70,7 +70,7 @@ func HandleMsgChannelOpenTry(ctx sdk.Context, k keeper.Keeper, msg types.MsgChan
// HandleMsgChannelOpenAck defines the sdk.Handler for MsgChannelOpenAck
func HandleMsgChannelOpenAck(ctx sdk.Context, k keeper.Keeper, msg types.MsgChannelOpenAck) (*sdk.Result, error) {
err := k.ChanOpenAck(
ctx, msg.PortID, msg.ChannelID, msg.CounterpartyVersion, msg.ProofTry, msg.ProofHeight,
ctx, msg.PortID, msg.ChannelID, msg.ChannelCap, msg.CounterpartyVersion, msg.ProofTry, msg.ProofHeight,
)
if err != nil {
return nil, err
Expand All @@ -96,7 +96,7 @@ func HandleMsgChannelOpenAck(ctx sdk.Context, k keeper.Keeper, msg types.MsgChan

// HandleMsgChannelOpenConfirm defines the sdk.Handler for MsgChannelOpenConfirm
func HandleMsgChannelOpenConfirm(ctx sdk.Context, k keeper.Keeper, msg types.MsgChannelOpenConfirm) (*sdk.Result, error) {
err := k.ChanOpenConfirm(ctx, msg.PortID, msg.ChannelID, msg.ProofAck, msg.ProofHeight)
err := k.ChanOpenConfirm(ctx, msg.PortID, msg.ChannelID, msg.ChannelCap, msg.ProofAck, msg.ProofHeight)
if err != nil {
return nil, err
}
Expand All @@ -121,7 +121,7 @@ func HandleMsgChannelOpenConfirm(ctx sdk.Context, k keeper.Keeper, msg types.Msg

// HandleMsgChannelCloseInit defines the sdk.Handler for MsgChannelCloseInit
func HandleMsgChannelCloseInit(ctx sdk.Context, k keeper.Keeper, msg types.MsgChannelCloseInit) (*sdk.Result, error) {
err := k.ChanCloseInit(ctx, msg.PortID, msg.ChannelID)
err := k.ChanCloseInit(ctx, msg.PortID, msg.ChannelID, msg.ChannelCap)
if err != nil {
return nil, err
}
Expand All @@ -146,7 +146,7 @@ func HandleMsgChannelCloseInit(ctx sdk.Context, k keeper.Keeper, msg types.MsgCh

// HandleMsgChannelCloseConfirm defines the sdk.Handler for MsgChannelCloseConfirm
func HandleMsgChannelCloseConfirm(ctx sdk.Context, k keeper.Keeper, msg types.MsgChannelCloseConfirm) (*sdk.Result, error) {
err := k.ChanCloseConfirm(ctx, msg.PortID, msg.ChannelID, msg.ProofInit, msg.ProofHeight)
err := k.ChanCloseConfirm(ctx, msg.PortID, msg.ChannelID, msg.ChannelCap, msg.ProofInit, msg.ProofHeight)
if err != nil {
return nil, err
}
Expand Down
104 changes: 47 additions & 57 deletions x/ibc/04-channel/keeper/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/capability"
connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection"
connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported"
porttypes "github.com/cosmos/cosmos-sdk/x/ibc/05-port/types"

"github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported"
"github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported"
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
)

// CounterpartyHops returns the connection hops of the counterparty channel.
Expand All @@ -33,38 +36,44 @@ func (k Keeper) ChanOpenInit(
connectionHops []string,
portID,
channelID string,
portCap capability.Capability,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passed in by caller as described in spec:

Note: If the host state machine is utilising object capability authentication (see ICS 005), all functions utilising ports take an additional capability parameter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct. Working on updating the spec.

counterparty types.Counterparty,
version string,
) error {
) (capability.Capability, error) {
// channel identifier and connection hop length checked on msg.ValidateBasic()

_, found := k.GetChannel(ctx, portID, channelID)
if found {
return sdkerrors.Wrap(types.ErrChannelExists, channelID)
return nil, sdkerrors.Wrap(types.ErrChannelExists, channelID)
}

connectionEnd, found := k.connectionKeeper.GetConnection(ctx, connectionHops[0])
if !found {
return sdkerrors.Wrap(connection.ErrConnectionNotFound, connectionHops[0])
return nil, sdkerrors.Wrap(connection.ErrConnectionNotFound, connectionHops[0])
}

if connectionEnd.GetState() == connectionexported.UNINITIALIZED {
return sdkerrors.Wrap(
return nil, sdkerrors.Wrap(
connection.ErrInvalidConnectionState,
"connection state cannot be UNINITIALIZED",
)
}

if !k.portKeeper.Authenticate(ctx, portCap, portID) {
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
return nil, sdkerrors.Wrap(porttypes.ErrInvalidPort, "caller does not own port capability")
}

channel := types.NewChannel(exported.INIT, order, counterparty, connectionHops, version)
k.SetChannel(ctx, portID, channelID, channel)

// TODO: blocked by #5542
// key := ""
// k.SetChannelCapability(ctx, portID, channelID, key)
capKey, err := k.scopedKeeper.NewCapability(ctx, ibctypes.ChannelCapabilityPath(portID, channelID))
if err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalidChannelCapability, err.Error())
}
k.SetNextSequenceSend(ctx, portID, channelID, 1)
k.SetNextSequenceRecv(ctx, portID, channelID, 1)

return nil
return capKey, nil
}

// ChanOpenTry is called by a module to accept the first step of a channel opening
Expand All @@ -75,12 +84,13 @@ func (k Keeper) ChanOpenTry(
connectionHops []string,
portID,
channelID string,
portCap capability.Capability,
counterparty types.Counterparty,
version,
counterpartyVersion string,
proofInit commitmentexported.Proof,
proofHeight uint64,
) error {
) (capability.Capability, error) {
// channel identifier and connection hop length checked on msg.ValidateBasic()

previousChannel, found := k.GetChannel(ctx, portID, channelID)
Expand All @@ -93,19 +103,17 @@ func (k Keeper) ChanOpenTry(
sdkerrors.Wrap(types.ErrInvalidChannel, "cannot relay connection attempt")
}

// TODO: blocked by #5542
// key := sdk.NewKVStoreKey(portID)
// if !k.portKeeper.Authenticate(key, portID) {
// return sdkerrors.Wrap(port.ErrInvalidPort, portID)
// }
if !k.portKeeper.Authenticate(ctx, portCap, portID) {
return nil, sdkerrors.Wrap(porttypes.ErrInvalidPort, "caller does not own port capability")
}

connectionEnd, found := k.connectionKeeper.GetConnection(ctx, connectionHops[0])
if !found {
return sdkerrors.Wrap(connection.ErrConnectionNotFound, connectionHops[0])
return nil, sdkerrors.Wrap(connection.ErrConnectionNotFound, connectionHops[0])
}

if connectionEnd.GetState() != connectionexported.OPEN {
return sdkerrors.Wrapf(
return nil, sdkerrors.Wrapf(
connection.ErrInvalidConnectionState,
"connection state is not OPEN (got %s)", connectionEnd.GetState().String(),
)
Expand Down Expand Up @@ -133,26 +141,28 @@ func (k Keeper) ChanOpenTry(
ctx, connectionEnd, proofHeight, proofInit,
counterparty.PortID, counterparty.ChannelID, expectedChannel,
); err != nil {
return err
return nil, err
}

k.SetChannel(ctx, portID, channelID, channel)

// TODO: blocked by #5542
// key := ""
// k.SetChannelCapability(ctx, portID, channelID, key)
capKey, err := k.scopedKeeper.NewCapability(ctx, ibctypes.ChannelCapabilityPath(portID, channelID))
if err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalidChannelCapability, err.Error())
}
k.SetNextSequenceSend(ctx, portID, channelID, 1)
k.SetNextSequenceRecv(ctx, portID, channelID, 1)

return nil
return capKey, nil
}

// ChanOpenAck is called by the handshake-originating module to acknowledge the
// acceptance of the initial request by the counterparty module on the other chain.
func (k Keeper) ChanOpenAck(
ctx sdk.Context,
portID,
channelID,
channelID string,
chanCap capability.Capability,
counterpartyVersion string,
proofTry commitmentexported.Proof,
proofHeight uint64,
Expand All @@ -169,11 +179,9 @@ func (k Keeper) ChanOpenAck(
)
}

// TODO: blocked by #5542
// key := sdk.NewKVStoreKey(portID)
// if !k.portKeeper.Authenticate(key, portID) {
// return sdkerrors.Wrap(port.ErrInvalidPort, portID)
// }
if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, ibctypes.ChannelCapabilityPath(portID, channelID)) {
return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel")
}

connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
if !found {
Expand Down Expand Up @@ -221,6 +229,7 @@ func (k Keeper) ChanOpenConfirm(
ctx sdk.Context,
portID,
channelID string,
chanCap capability.Capability,
proofAck commitmentexported.Proof,
proofHeight uint64,
) error {
Expand All @@ -236,16 +245,9 @@ func (k Keeper) ChanOpenConfirm(
)
}

// TODO: blocked by #5542
// capkey, found := k.GetChannelCapability(ctx, portID, channelID)
// if !found {
// return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, channelID)
// }

// key := sdk.NewKVStoreKey(capkey)
// if !k.portKeeper.Authenticate(key, portID) {
// return sdkerrors.Wrap(port.ErrInvalidPort, portID)
// }
if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, ibctypes.ChannelCapabilityPath(portID, channelID)) {
return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel")
}

connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
if !found {
Expand Down Expand Up @@ -296,17 +298,11 @@ func (k Keeper) ChanCloseInit(
ctx sdk.Context,
portID,
channelID string,
chanCap capability.Capability,
) error {
// TODO: blocked by #5542
// capkey, found := k.GetChannelCapability(ctx, portID, channelID)
// if !found {
// return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, channelID)
// }

// key := sdk.NewKVStoreKey(capkey)
// if !k.portKeeper.Authenticate(key, portID) {
// return sdkerrors.Wrap(port.ErrInvalidPort, portID)
// }
if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, ibctypes.ChannelCapabilityPath(portID, channelID)) {
return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel")
}

channel, found := k.GetChannel(ctx, portID, channelID)
if !found {
Expand Down Expand Up @@ -341,19 +337,13 @@ func (k Keeper) ChanCloseConfirm(
ctx sdk.Context,
portID,
channelID string,
chanCap capability.Capability,
proofInit commitmentexported.Proof,
proofHeight uint64,
) error {
// TODO: blocked by #5542
// capkey, found := k.GetChannelCapability(ctx, portID, channelID)
// if !found {
// return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, channelID)
// }

// key := sdk.NewKVStoreKey(capkey)
// if !k.portKeeper.Authenticate(key, portID) {
// return sdkerrors.Wrap(port.ErrInvalidPort, portID)
// }
if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, ibctypes.ChannelCapabilityPath(portID, channelID)) {
return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel")
}

channel, found := k.GetChannel(ctx, portID, channelID)
if !found {
Expand Down
22 changes: 4 additions & 18 deletions x/ibc/04-channel/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/capability"
"github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
)
Expand All @@ -19,20 +20,22 @@ type Keeper struct {
clientKeeper types.ClientKeeper
connectionKeeper types.ConnectionKeeper
portKeeper types.PortKeeper
scopedKeeper capability.ScopedKeeper
}

// NewKeeper creates a new IBC channel Keeper instance
func NewKeeper(
cdc *codec.Codec, key sdk.StoreKey,
clientKeeper types.ClientKeeper, connectionKeeper types.ConnectionKeeper,
portKeeper types.PortKeeper,
portKeeper types.PortKeeper, scopedKeeper capability.ScopedKeeper,
) Keeper {
return Keeper{
storeKey: key,
cdc: cdc,
clientKeeper: clientKeeper,
connectionKeeper: connectionKeeper,
portKeeper: portKeeper,
scopedKeeper: scopedKeeper,
}
}

Expand Down Expand Up @@ -61,23 +64,6 @@ func (k Keeper) SetChannel(ctx sdk.Context, portID, channelID string, channel ty
store.Set(ibctypes.KeyChannel(portID, channelID), bz)
}

// GetChannelCapability gets a channel's capability key from the store
func (k Keeper) GetChannelCapability(ctx sdk.Context, portID, channelID string) (string, bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(ibctypes.KeyChannelCapabilityPath(portID, channelID))
if bz == nil {
return "", false
}

return string(bz), true
}

// SetChannelCapability sets a channel's capability key to the store
func (k Keeper) SetChannelCapability(ctx sdk.Context, portID, channelID string, key string) {
store := ctx.KVStore(k.storeKey)
store.Set(ibctypes.KeyChannelCapabilityPath(portID, channelID), []byte(key))
}

// GetNextSequenceSend gets a channel's next send sequence from the store
func (k Keeper) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) {
store := ctx.KVStore(k.storeKey)
Expand Down
15 changes: 8 additions & 7 deletions x/ibc/04-channel/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ var (
ErrInvalidChannelState = sdkerrors.Register(SubModuleName, 4, "invalid channel state")
ErrInvalidChannelOrdering = sdkerrors.Register(SubModuleName, 5, "invalid channel ordering")
ErrInvalidCounterparty = sdkerrors.Register(SubModuleName, 6, "invalid counterparty channel")
ErrChannelCapabilityNotFound = sdkerrors.Register(SubModuleName, 7, "channel capability not found")
ErrSequenceSendNotFound = sdkerrors.Register(SubModuleName, 8, "sequence send not found")
ErrSequenceReceiveNotFound = sdkerrors.Register(SubModuleName, 9, "sequence receive not found")
ErrInvalidPacket = sdkerrors.Register(SubModuleName, 10, "invalid packet")
ErrPacketTimeout = sdkerrors.Register(SubModuleName, 11, "packet timeout")
ErrTooManyConnectionHops = sdkerrors.Register(SubModuleName, 12, "too many connection hops")
ErrAcknowledgementTooLong = sdkerrors.Register(SubModuleName, 13, "acknowledgement too long")
ErrInvalidChannelCapability = sdkerrors.Register(SubModuleName, 7, "invalid channel capability")
ErrChannelCapabilityNotFound = sdkerrors.Register(SubModuleName, 8, "channel capability not found")
ErrSequenceSendNotFound = sdkerrors.Register(SubModuleName, 9, "sequence send not found")
ErrSequenceReceiveNotFound = sdkerrors.Register(SubModuleName, 10, "sequence receive not found")
ErrInvalidPacket = sdkerrors.Register(SubModuleName, 11, "invalid packet")
ErrPacketTimeout = sdkerrors.Register(SubModuleName, 12, "packet timeout")
ErrTooManyConnectionHops = sdkerrors.Register(SubModuleName, 13, "too many connection hops")
ErrAcknowledgementTooLong = sdkerrors.Register(SubModuleName, 14, "acknowledgement too long")
)
Loading