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

Alex ibc spike2 #231

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ type WasmApp struct {
// make scoped keepers public for test purposes
ScopedIBCKeeper capabilitykeeper.ScopedKeeper
ScopedTransferKeeper capabilitykeeper.ScopedKeeper
ScopedWasmKeeper capabilitykeeper.ScopedKeeper

// the module manager
mm *module.Manager
Expand Down Expand Up @@ -414,6 +415,7 @@ func NewWasmApp(

app.ScopedIBCKeeper = scopedIBCKeeper
app.ScopedTransferKeeper = scopedTransferKeeper
app.ScopedWasmKeeper = scopedWasmKeeper

return app
}
Expand Down
58 changes: 28 additions & 30 deletions x/wasm/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,34 +88,32 @@ var (
)

type (
GenesisState = types.GenesisState
Code = types.Code
Contract = types.Contract
MsgStoreCode = types.MsgStoreCode
MsgInstantiateContract = types.MsgInstantiateContract
MsgExecuteContract = types.MsgExecuteContract
MsgMigrateContract = types.MsgMigrateContract
MsgUpdateAdmin = types.MsgUpdateAdmin
MsgClearAdmin = types.MsgClearAdmin
MsgWasmIBCCall = types.MsgWasmIBCCall
Model = types.Model
CodeInfo = types.CodeInfo
ContractInfo = types.ContractInfo
CreatedAt = types.AbsoluteTxPosition
WasmConfig = types.WasmConfig
WasmIBCContractPacketAcknowledgement = types.WasmIBCContractPacketAcknowledgement
WasmIBCContractPacketData = types.WasmIBCContractPacketData
MessageHandler = keeper.MessageHandler
BankEncoder = keeper.BankEncoder
CustomEncoder = keeper.CustomEncoder
StakingEncoder = keeper.StakingEncoder
WasmEncoder = keeper.WasmEncoder
MessageEncoders = keeper.MessageEncoders
Keeper = keeper.Keeper
ContractInfoWithAddress = keeper.ContractInfoWithAddress
GetCodeResponse = keeper.GetCodeResponse
ListCodeResponse = keeper.ListCodeResponse
QueryHandler = keeper.QueryHandler
CustomQuerier = keeper.CustomQuerier
QueryPlugins = keeper.QueryPlugins
GenesisState = types.GenesisState
Code = types.Code
Contract = types.Contract
MsgStoreCode = types.MsgStoreCode
MsgInstantiateContract = types.MsgInstantiateContract
MsgExecuteContract = types.MsgExecuteContract
MsgMigrateContract = types.MsgMigrateContract
MsgUpdateAdmin = types.MsgUpdateAdmin
MsgClearAdmin = types.MsgClearAdmin
MsgWasmIBCCall = types.MsgWasmIBCCall
Model = types.Model
CodeInfo = types.CodeInfo
ContractInfo = types.ContractInfo
CreatedAt = types.AbsoluteTxPosition
WasmConfig = types.WasmConfig
MessageHandler = keeper.MessageHandler
BankEncoder = keeper.BankEncoder
CustomEncoder = keeper.CustomEncoder
StakingEncoder = keeper.StakingEncoder
WasmEncoder = keeper.WasmEncoder
MessageEncoders = keeper.MessageEncoders
Keeper = keeper.Keeper
ContractInfoWithAddress = keeper.ContractInfoWithAddress
GetCodeResponse = keeper.GetCodeResponse
ListCodeResponse = keeper.ListCodeResponse
QueryHandler = keeper.QueryHandler
CustomQuerier = keeper.CustomQuerier
QueryPlugins = keeper.QueryPlugins
)
5 changes: 1 addition & 4 deletions x/wasm/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,7 @@ func handleClearContractAdmin(ctx sdk.Context, k Keeper, msg *MsgClearAdmin) (*s
}

func handleIBCCall(ctx sdk.Context, k Keeper, msg *MsgWasmIBCCall) (*sdk.Result, error) {
if err := k.IBCCallContract(ctx, msg.SourcePort, msg.SourceChannel,
msg.Sender, msg.DestContractAddr, msg.TimeoutHeight,
msg.TimeoutTimestamp, msg.Msg,
); err != nil {
if err := k.IBCCallFromContract(ctx, msg.SourcePort, msg.SourceChannel, msg.Sender, msg.TimeoutHeight, msg.TimeoutTimestamp, msg.Msg); err != nil {
return nil, err
}

Expand Down
176 changes: 144 additions & 32 deletions x/wasm/ibc.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package wasm

import (
"github.com/CosmWasm/wasmd/x/wasm/internal/keeper/cosmwasm"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
"github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
"github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)

type IBCHandler struct {
Expand All @@ -15,54 +18,102 @@ func NewIBCHandler(keeper Keeper) IBCHandler {
return IBCHandler{keeper: keeper}
}

func (i IBCHandler) OnChanOpenInit(ctx sdk.Context, order types.Order, connectionHops []string, portID string, channelID string, channelCap *capabilitytypes.Capability, counterParty types.Counterparty, version string) error {
func (i IBCHandler) OnChanOpenInit(ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID string, channelID string, channelCap *capabilitytypes.Capability, counterParty channeltypes.Counterparty, version string) error {
// ensure port, version, capability
panic("implement me")

contractAddr, err := ContractFromPortID(portID)
if err != nil {
return sdkerrors.Wrapf(err, "contract port id")
}

_, err = i.keeper.AcceptChannel(ctx, contractAddr, order, version, connectionHops, cosmwasm.IBCInfo{
PortID: portID,
ChannelID: channelID,
})
if err != nil {
return err
}
// Claim channel capability passed back by IBC module
if err := i.keeper.ClaimCapability(ctx, channelCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, err.Error())
}
return nil
}

func (i IBCHandler) OnChanOpenTry(ctx sdk.Context, order types.Order, connectionHops []string, portID, channelID string, channelCap *capabilitytypes.Capability, counterparty types.Counterparty, version, counterpartyVersion string) error {
func (i IBCHandler) OnChanOpenTry(ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID, channelID string, channelCap *capabilitytypes.Capability, counterParty channeltypes.Counterparty, version, counterpartyVersion string) error {
// ensure port, version, capability
// do we require an ORDERED channel?
panic("implement me")
contractAddr, err := ContractFromPortID(portID)
if err != nil {
return sdkerrors.Wrapf(err, "contract port id")
}

restrictCounterpartyVersions, err := i.keeper.AcceptChannel(ctx, contractAddr, order, version, connectionHops, cosmwasm.IBCInfo{
PortID: portID,
ChannelID: channelID,
})
if err != nil {
return err
}
if len(restrictCounterpartyVersions) != 0 {
var found bool
for _, accept := range restrictCounterpartyVersions {
if accept == counterpartyVersion {
found = true
break
}
}
if !found {
return sdkerrors.Wrapf(types.ErrInvalidCounterparty, "not in supported versions: %q", restrictCounterpartyVersions)
}
}

// Claim channel capability passed back by IBC module
if err := i.keeper.ClaimCapability(ctx, channelCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, err.Error())
}
return nil
}

func (i IBCHandler) OnChanOpenAck(ctx sdk.Context, portID, channelID string, counterpartyVersion string) error {
// ensure port, version, capability
panic("implement me")
// anything to do? We are not opening channels from wasm contracts
return nil
}

func (i IBCHandler) OnChanOpenConfirm(ctx sdk.Context, portID, channelID string) error {
// anything to do?
panic("implement me")
//contractAddr, err := ContractFromPortID(portID)
//if err != nil {
// return sdkerrors.Wrapf(err, "contract port id")
//}
//return i.keeper.OnChannelOpen(ctx, contractAddr, cosmwasm.IBCInfo{PortID: portID, ChannelID: channelID})
// any events to send?
panic("not implemented")
}

func (i IBCHandler) OnChanCloseInit(ctx sdk.Context, portID, channelID string) error {
// we better not close a channel
panic("implement me")
// we can let contracts close channels so we can play back this to the contract
panic("not implemented")
}

func (i IBCHandler) OnChanCloseConfirm(ctx sdk.Context, portID, channelID string) error {
// anything to do
panic("implement me")
}
// counterparty has closed the channel

func (i IBCHandler) OnRecvPacket(ctx sdk.Context, packet types.Packet) (*sdk.Result, []byte, error) {
// start calling keeper
var data WasmIBCContractPacketData
if err := ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
return nil, nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error())
}
acknowledgement := WasmIBCContractPacketAcknowledgement{Success: true}
//contractAddr, err := ContractFromPortID(portID)
//if err != nil {
// return sdkerrors.Wrapf(err, "contract port id")
//}
//return i.keeper.OnChannelClose(ctx, contractAddr, cosmwasm.IBCInfo{PortID: portID, ChannelID: channelID})
// any events to send?
panic("not implemented")
}

func (i IBCHandler) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) (*sdk.Result, []byte, error) {
contractAddr, err := ContractFromPortID(packet.DestinationPort)
if err != nil {
return nil, nil, sdkerrors.Wrapf(err, "contract port id")
}
if err := i.keeper.OnRecvPacket(ctx, contractAddr, data); err != nil {
acknowledgement = WasmIBCContractPacketAcknowledgement{
Success: false,
Error: err.Error(),
}
msgBz, err := i.keeper.OnRecvPacket(ctx, contractAddr, packet.Data, ibcInfoFromPacket(packet))
if err != nil {
return nil, nil, err
}

// todo: send proper events
Expand All @@ -77,13 +128,74 @@ func (i IBCHandler) OnRecvPacket(ctx sdk.Context, packet types.Packet) (*sdk.Res

return &sdk.Result{
Events: ctx.EventManager().Events().ToABCIEvents(),
}, sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(acknowledgement)), nil
}, msgBz, nil
}

func (i IBCHandler) OnAcknowledgementPacket(ctx sdk.Context, packet types.Packet, acknowledgement []byte) (*sdk.Result, error) {
panic("implement me")
func (i IBCHandler) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte) (*sdk.Result, error) {
contractAddr, err := ContractFromPortID(packet.DestinationPort)
if err != nil {
return nil, sdkerrors.Wrapf(err, "contract port id")
}

err = i.keeper.OnAckPacket(ctx, contractAddr, packet.Data, acknowledgement, ibcInfoFromPacket(packet))
if err != nil {
return nil, err
}

//ctx.EventManager().EmitEvent(
// sdk.NewEvent(
// types.EventTypePacket,
// sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
// sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver),
// sdk.NewAttribute(types.AttributeKeyValue, data.Amount.String()),
// sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", ack.Success)),
// ),
//)

//if !ack.Success {
// ctx.EventManager().EmitEvent(
// sdk.NewEvent(
// types.EventTypePacket,
// sdk.NewAttribute(types.AttributeKeyAckError, ack.Error),
// ),
// )
//}

return &sdk.Result{
Events: ctx.EventManager().Events().ToABCIEvents(),
}, nil

}

func (i IBCHandler) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet) (*sdk.Result, error) {
contractAddr, err := ContractFromPortID(packet.DestinationPort)
if err != nil {
return nil, sdkerrors.Wrapf(err, "contract port id")
}
err = i.keeper.OnTimeoutPacket(ctx, contractAddr, packet.Data, ibcInfoFromPacket(packet))
if err != nil {
return nil, err
}

//ctx.EventManager().EmitEvent(
// sdk.NewEvent(
// types.EventTypeTimeout,
// sdk.NewAttribute(types.AttributeKeyRefundReceiver, data.Sender),
// sdk.NewAttribute(types.AttributeKeyRefundValue, data.Amount.String()),
// sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
// ),
//)

return &sdk.Result{
Events: ctx.EventManager().Events().ToABCIEvents(),
}, nil

}

func (i IBCHandler) OnTimeoutPacket(ctx sdk.Context, packet types.Packet) (*sdk.Result, error) {
panic("implement me")
func ibcInfoFromPacket(packet channeltypes.Packet) cosmwasm.IBCInfo {
return cosmwasm.IBCInfo{
PortID: packet.DestinationPort,
ChannelID: packet.DestinationChannel,
Packet: cosmwasm.NewIBCPacketInfo(packet.Sequence, packet.SourcePort, packet.SourceChannel, packet.TimeoutHeight, packet.TimeoutTimestamp),
}
}
1 change: 1 addition & 0 deletions x/wasm/internal/keeper/cosmwasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
types that belong to go-cosmwasm. copied here temporary for faster dev feedback.
35 changes: 35 additions & 0 deletions x/wasm/internal/keeper/cosmwasm/contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package cosmwasm

import (
"encoding/json"

wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

type OnReceiveIBCResponse struct {
Copy link
Member Author

Choose a reason for hiding this comment

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

These are three clear entrypoints. I would like to make OnAcknowledgeIBCResponse / OnTimeoutIBCResponse => OnAcknowledgeIBCSuccess / OnAcknowledgeIBCError as I see no difference in handling a timeout from a rejected packet (it failed, undo state changes)

Copy link
Contributor

Choose a reason for hiding this comment

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

🤔 Success and Error are a bit ambiguous as an Ack can contain an application level error message.

Copy link
Member Author

Choose a reason for hiding this comment

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

Exactly, the IBC/wasmd entry points make sense for the protocol level.

I would like to use more semantically interesting ones for the contracts.
And yes, I am only thinking of the interface we expose to the contracts, and figure we provide an adaptor between the official IBC and our desired API.

Messages []sdk.Msg `json:"messages"`

Acknowledgement json.RawMessage
// log message to return over abci interface
Log []wasmTypes.LogAttribute `json:"log"`
}

type OnAcknowledgeIBCResponse struct {
Messages []sdk.Msg `json:"messages"`

// log message to return over abci interface
Log []wasmTypes.LogAttribute `json:"log"`
}
type OnTimeoutIBCResponse struct {
Messages []sdk.Msg `json:"messages"`

// log message to return over abci interface
Log []wasmTypes.LogAttribute `json:"log"`
}

type AcceptChannelResponse struct {
Copy link
Member Author

Choose a reason for hiding this comment

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

This would be one more contract callback? for the OnTryInitChannel or whatever it is called?

Copy link
Contributor

Choose a reason for hiding this comment

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

I have used this for OnChanOpenInit OnChanOpenTry but channel handshake was only touched in this spike.
My idea was to have the contracts engaged here as well. For example if a contract is end-of-life (burned, escrow released,...) it would make sense to not accept any new channels.

Result bool `json:"result"`
Reason string `json:"reason"`
RestrictCounterpartyVersions []string `json:"accepted_counterpary_versions"`
}
Loading