forked from cosmos/ibc-go
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: documentation added for callbacks middleware (backport cosmos#4370
) (cosmos#4458) * docs: documentation added for callbacks middleware (cosmos#4370) * docs: added callbacks middleware overview * docs: added interfaces.md to callbacks * docs: added usage to callback docs * docs: added events to callbacks docs * docs: integration docs for callbacks added * docs: added user defined gas limit to callbacks/usage * docs: renamed usage.md -> end-users.md * docs: added callbacks docs to config.js * docs: improved interfaces slightly * docs: added order frontmatter to markdown files * 2-space tabs for better readability * docs: improved callbacks/interfaces.md * docs: improved callbacks/overview.md * docs: improved callbacks/integration.md * docs: improved callbacks/end-users.md * docs: improved callbacks/interfaces.md * docs: added callbacks diagrams * docs: added the new diagrams to overview.md * docs: added improvements to callback docs' * docs: gas.md added to callbacks docs * docs: minor grammar improvement * docs: fixed another grammar error in callbacks * docs: implemented review items * code formatting and fix typo * docs: improved the callback diagram --------- Co-authored-by: Carlos Rodriguez <[email protected]> (cherry picked from commit d0f7773) * add ADR 8 files --------- Co-authored-by: srdtrk <[email protected]> Co-authored-by: Carlos Rodriguez <[email protected]>
- Loading branch information
1 parent
23b4a24
commit 812a364
Showing
11 changed files
with
1,239 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
421 changes: 421 additions & 0 deletions
421
docs/architecture/adr-008-app-caller-cbs/adr-008-app-caller-cbs.md
Large diffs are not rendered by default.
Oops, something went wrong.
223 changes: 223 additions & 0 deletions
223
docs/architecture/adr-008-app-caller-cbs/transfer-callback-scaffold.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
# Scaffold for ICS20 Callback Middleware | ||
|
||
The following is a very simple scaffold for middleware that implements actor callbacks for transfer channels. Since a channel does not need to be owned by a single actor, the handshake callbacks are no-ops. The packet callbacks will call into the relevant actor. For `OnRecvPacket`, this will be `data.Receiver`, and for `OnAcknowledgePacket` and `OnTimeoutPacket` this will be `data.Sender`. | ||
|
||
The exact nature of the callbacks to the smart contract will depend on the environment in question (e.g. cosmwasm, evm). Thus the place where the callbacks to the smart contract are stubbed out and commented so that it can be completed by implementers for the specific environment they are targetting. | ||
|
||
Implementers may wish to support callbacks to more IBC applications by adding a switch statement to unmarshal the specific packet data types they wish to support and passing them into the smart contract callback functions. | ||
|
||
### Scaffold Middleware | ||
|
||
```go | ||
package callbacks | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" | ||
|
||
transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" | ||
clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" | ||
channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" | ||
porttypes "github.com/cosmos/ibc-go/v6/modules/core/05-port/types" | ||
"github.com/cosmos/ibc-go/v6/modules/core/exported" | ||
) | ||
|
||
var _ porttypes.Middleware = &IBCMiddleware{} | ||
|
||
// IBCMiddleware implements the ICS26 callbacks for the fee middleware given the | ||
// fee keeper and the underlying application. | ||
type IBCMiddleware struct { | ||
app porttypes.IBCModule | ||
chanWrapper porttypes.ICS4Wrapper | ||
} | ||
|
||
// NewIBCMiddleware creates a new IBCMiddlware given the keeper and underlying application | ||
func NewIBCMiddleware(app porttypes.IBCModule, chanWrapper porttypes.ICS4Wrapper) IBCMiddleware { | ||
return IBCMiddleware{ | ||
app: app, | ||
chanWrapper: chanWrapper, | ||
} | ||
} | ||
|
||
// OnChanOpenInit implements the IBCMiddleware interface | ||
func (im IBCMiddleware) OnChanOpenInit( | ||
ctx sdk.Context, | ||
order channeltypes.Order, | ||
connectionHops []string, | ||
portID string, | ||
channelID string, | ||
chanCap *capabilitytypes.Capability, | ||
counterparty channeltypes.Counterparty, | ||
version string, | ||
) (string, error) { | ||
// call underlying app's OnChanOpenInit callback | ||
return im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, | ||
chanCap, counterparty, version) | ||
} | ||
|
||
// OnChanOpenTry implements the IBCMiddleware interface | ||
// If the channel is not fee enabled the underlying application version will be returned | ||
// If the channel is fee enabled we merge the underlying application version with the ics29 version | ||
func (im IBCMiddleware) OnChanOpenTry( | ||
ctx sdk.Context, | ||
order channeltypes.Order, | ||
connectionHops []string, | ||
portID, | ||
channelID string, | ||
chanCap *capabilitytypes.Capability, | ||
counterparty channeltypes.Counterparty, | ||
counterpartyVersion string, | ||
) (string, error) { | ||
// call underlying app's OnChanOpenTry callback | ||
return im.app.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, counterpartyVersion) | ||
} | ||
|
||
// OnChanOpenAck implements the IBCMiddleware interface | ||
func (im IBCMiddleware) OnChanOpenAck( | ||
ctx sdk.Context, | ||
portID, | ||
channelID string, | ||
counterpartyChannelID string, | ||
counterpartyVersion string, | ||
) error { | ||
// call underlying app's OnChanOpenAck callback | ||
return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) | ||
} | ||
|
||
// OnChanOpenConfirm implements the IBCMiddleware interface | ||
func (im IBCMiddleware) OnChanOpenConfirm( | ||
ctx sdk.Context, | ||
portID, | ||
channelID string, | ||
) error { | ||
// call underlying app's OnChanOpenConfirm callback. | ||
return im.app.OnChanOpenConfirm(ctx, portID, channelID) | ||
} | ||
|
||
// OnChanCloseInit implements the IBCMiddleware interface | ||
func (im IBCMiddleware) OnChanCloseInit( | ||
ctx sdk.Context, | ||
portID, | ||
channelID string, | ||
) error { | ||
// call underlying app's OnChanCloseInit callback. | ||
return im.app.OnChanCloseInit(ctx, portID, channelID) | ||
} | ||
|
||
// OnChanCloseConfirm implements the IBCMiddleware interface | ||
func (im IBCMiddleware) OnChanCloseConfirm( | ||
ctx sdk.Context, | ||
portID, | ||
channelID string, | ||
) error { | ||
// call underlying app's OnChanCloseConfirm callback. | ||
return im.app.OnChanCloseConfirm(ctx, portID, channelID) | ||
} | ||
|
||
// OnRecvPacket implements the IBCMiddleware interface. | ||
// If fees are not enabled, this callback will default to the ibc-core packet callback | ||
func (im IBCMiddleware) OnRecvPacket( | ||
ctx sdk.Context, | ||
packet channeltypes.Packet, | ||
relayer sdk.AccAddress, | ||
) exported.Acknowledgement { | ||
// first do the underlying ibc app callback | ||
ack := im.app.OnRecvPacket(ctx, packet, relayer) | ||
|
||
// postprocess the ibc application by executing a callback to the receiver smart contract | ||
// if the receiver of the transfer packet is a smart contract | ||
var data transfertypes.FungibleTokenPacketData | ||
if err := transfertypes.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err == nil { | ||
// check if data.Receiver is a smart contract address | ||
// if it is a smart contract address | ||
// then call the smart-contract defined callback for RecvPacket and pass in the transfer packet data | ||
// if the callback returns an error, then return an error acknowledgement | ||
if isSmartContract(data.Receiver) { | ||
err := data.Receiver.recvPacketCallback(data) | ||
if err != nil { | ||
return channeltypes.NewErrorAcknowledgement(err) | ||
} | ||
} | ||
} | ||
return ack | ||
} | ||
|
||
// OnAcknowledgementPacket implements the IBCMiddleware interface | ||
// If fees are not enabled, this callback will default to the ibc-core packet callback | ||
func (im IBCMiddleware) OnAcknowledgementPacket( | ||
ctx sdk.Context, | ||
packet channeltypes.Packet, | ||
acknowledgement []byte, | ||
relayer sdk.AccAddress, | ||
) error { | ||
// call underlying callback | ||
err := im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) | ||
|
||
// postprocess the ibc application by executing a callback to the sender smart contract | ||
// if the sender of the transfer packet is a smart contract | ||
var data transfertypes.FungibleTokenPacketData | ||
if err := transfertypes.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err == nil { | ||
// check if data.Sender is a smart contract address | ||
// if it is a smart contract address | ||
// then call the smart-contract defined callback for RecvPacket and pass in the transfer packet data | ||
// if the callback returns an error, then return an error | ||
if isSmartContract(data.Sender) { | ||
data.Sender.ackPacketCallback(data) | ||
} | ||
} | ||
return err | ||
} | ||
|
||
// OnTimeoutPacket implements the IBCMiddleware interface | ||
// If fees are not enabled, this callback will default to the ibc-core packet callback | ||
func (im IBCMiddleware) OnTimeoutPacket( | ||
ctx sdk.Context, | ||
packet channeltypes.Packet, | ||
relayer sdk.AccAddress, | ||
) error { | ||
// call underlying callback | ||
err := im.app.OnTimeoutPacket(ctx, packet, relayer) | ||
|
||
// postprocess the ibc application by executing a callback to the sender smart contract | ||
// if the sender of the transfer packet is a smart contract | ||
var data transfertypes.FungibleTokenPacketData | ||
if err := transfertypes.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err == nil { | ||
// check if data.Sender is a smart contract address | ||
// if it is a smart contract address | ||
// then call the smart-contract defined callback for RecvPacket and pass in the transfer packet data | ||
// if the callback returns an error, then return an error | ||
if isSmartContract(data.Sender) { | ||
data.Sender.timeoutPacketCallback(data) | ||
} | ||
} | ||
return err | ||
} | ||
|
||
// SendPacket implements the ICS4 Wrapper interface | ||
func (im IBCMiddleware) SendPacket( | ||
ctx sdk.Context, | ||
chanCap *capabilitytypes.Capability, | ||
sourcePort string, | ||
sourceChannel string, | ||
timeoutHeight clienttypes.Height, | ||
timeoutTimestamp uint64, | ||
data []byte, | ||
) (uint64, error) { | ||
return im.chanWrapper.SendPacket(ctx, chanCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data) | ||
} | ||
|
||
// WriteAcknowledgement implements the ICS4 Wrapper interface | ||
func (im IBCMiddleware) WriteAcknowledgement( | ||
ctx sdk.Context, | ||
chanCap *capabilitytypes.Capability, | ||
packet exported.PacketI, | ||
ack exported.Acknowledgement, | ||
) error { | ||
return im.chanWrapper.WriteAcknowledgement(ctx, chanCap, packet, ack) | ||
} | ||
|
||
// GetAppVersion returns the application version of the underlying application | ||
func (im IBCMiddleware) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { | ||
return im.chanWrapper.GetAppVersion(ctx, portID, channelID) | ||
} | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<!-- | ||
order: 5 | ||
--> | ||
|
||
# Usage | ||
|
||
This section explains how to use the callbacks middleware from the perspective of an IBC Actor. Callbacks middleware provides two types of callbacks: | ||
|
||
- Source callbacks: | ||
- `SendPacket` callback | ||
- `OnAcknowledgementPacket` callback | ||
- `OnTimeoutPacket` callback | ||
- Destination callbacks: | ||
- `ReceivePacket` callback | ||
|
||
For a given channel, the source callbacks are supported if the source chain has the callbacks middleware wired up in the channel's IBC stack. Similarly, the destination callbacks are supported if the destination chain has the callbacks middleware wired up in the channel's IBC stack. | ||
|
||
::: tip | ||
Callbacks are always executed after the packet has been processed by the underlying IBC module. | ||
::: | ||
|
||
::: warning | ||
If the underlying application module is doing an asynchronous acknowledgement on packet receive (for example, if the [packet forward middleware](https://github.com/cosmos/ibc-apps/tree/main/middleware/packet-forward-middleware) is in the stack, and is being used by this packet), then the callbacks middleware will execute the `ReceivePacket` callback after the acknowledgement has been received. | ||
::: | ||
|
||
## Source Callbacks | ||
|
||
Source callbacks are natively supported in the following ibc modules (if they are wrapped by the callbacks middleware): | ||
|
||
- `transfer` | ||
- `icacontroller` | ||
|
||
To have your source callbacks be processed by the callbacks middleware, you must set the memo in the application's packet data to the following format: | ||
|
||
```jsonc | ||
{ | ||
"src_callback": { | ||
"address": "callbackAddressString", | ||
// optional | ||
"gas_limit": "userDefinedGasLimitString", | ||
} | ||
} | ||
``` | ||
|
||
## Destination Callbacks | ||
|
||
Destination callbacks are natively only supported in the transfer module. Note that wrapping icahost is not supported. This is because icahost should be able to execute an arbitrary transaction anyway, and can call contracts or modules directly. | ||
|
||
To have your destination callbacks processed by the callbacks middleware, you must set the memo in the application's packet data to the following format: | ||
|
||
```jsonc | ||
{ | ||
"dest_callback": { | ||
"address": "callbackAddressString", | ||
// optional | ||
"gas_limit": "userDefinedGasLimitString", | ||
} | ||
} | ||
``` | ||
|
||
Note that a packet can have both a source and destination callback. | ||
|
||
```jsonc | ||
{ | ||
"src_callback": { | ||
"address": "callbackAddressString", | ||
// optional | ||
"gas_limit": "userDefinedGasLimitString", | ||
}, | ||
"dest_callback": { | ||
"address": "callbackAddressString", | ||
// optional | ||
"gas_limit": "userDefinedGasLimitString", | ||
} | ||
} | ||
``` | ||
|
||
# User Defined Gas Limit | ||
|
||
User defined gas limit was added for the following reasons: | ||
|
||
- To prevent callbacks from blocking packet lifecycle. | ||
- To prevent relayers from being able to DOS the callback execution by sending a packet with a low amount of gas. | ||
|
||
::: tip | ||
There is a chain wide parameter that sets the maximum gas limit that a user can set for a callback. This is to prevent a user from setting a gas limit that is too high for relayers. If the `"gas_limit"` is not set in the packet memo, then the maximum gas limit is used. | ||
::: | ||
|
||
These goals are achieved by creating a minimum gas amount required for callback execution. If the relayer provides at least the minimum gas limit for the callback execution, then the packet lifecycle will not be blocked if the callback runs out of gas during execution, and the callback cannot be retried. If the relayer does not provided the minimum amount of gas and the callback executions runs out of gas, the entire tx is reverted and it may be executed again. | ||
|
||
::: tip | ||
`SendPacket` callback is always reverted if the callback execution fails or returns an error for any reason. This is so that the packet is not sent if the callback execution fails. | ||
::: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<!-- | ||
order: 4 | ||
--> | ||
|
||
# Events | ||
|
||
An overview of all events related to the callbacks middleware. There are two types of events, `"ibc_src_callback"` and `"ibc_dest_callback"`. | ||
|
||
## Shared Attributes | ||
|
||
Both of these event types share the following attributes: | ||
|
||
| **Attribute Key** | **Attribute Values** | **Optional** | | ||
|:-------------------------:|:---------------------------------------------------------------------------------------:|:------------------:| | ||
| module | "ibccallbacks" | | | ||
| callback_type | **One of**: "send_packet", "acknowledgement_packet", "timeout_packet", "receive_packet" | | | ||
| callback_address | string | | | ||
| callback_exec_gas_limit | string (parsed from uint64) | | | ||
| callback_commit_gas_limit | string (parsed from uint64) | | | ||
| packet_sequence | string (parsed from uint64) | | | ||
| callback_result | **One of**: "success", "failure" | | | ||
| callback_error | string (parsed from callback err) | Yes, if err != nil | | ||
|
||
## `ibc_src_callback` Attributes | ||
|
||
| **Attribute Key** | **Attribute Values** | | ||
|:------------------:|:------------------------:| | ||
| packet_src_port | string (sourcePortID) | | ||
| packet_src_channel | string (sourceChannelID) | | ||
|
||
## `ibc_dest_callback` Attributes | ||
|
||
| **Attribute Key** | **Attribute Values** | | ||
|:-------------------:|:------------------------:| | ||
| packet_dest_port | string (destPortID) | | ||
| packet_dest_channel | string (destChannelID) | |
Oops, something went wrong.