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

Ack callbacks for IBC transfers #3589

Merged
merged 26 commits into from
Dec 16, 2022
Merged

Conversation

nicolaslara
Copy link
Contributor

@nicolaslara nicolaslara commented Dec 1, 2022

What is the purpose of the change

This PR introduces the notion of ack callbacks.

Brief Changelog

  • Any transfer packet can now include a callback contract in its memo
  • When an ack is received, the specified contract will receive a sudo message with the information about the ack

Testing and Verifying

ToDo: add generic tests for this beyond the existing test in the crosschain swaps branch

Documentation and Release Note

  • Does this pull request introduce a new feature or user-facing behavior changes? (yes)
  • Is a relevant changelog entry added to the Unreleased section in CHANGELOG.md? (no)
  • How is the feature or change documented? (spec in the readme)

@github-actions github-actions bot added C:app-wiring Changes to the app folder C:docs Improvements or additions to documentation labels Dec 1, 2022
@nicolaslara nicolaslara marked this pull request as ready for review December 2, 2022 07:28
@nicolaslara
Copy link
Contributor Author

Added some tests and general cleanup. Does it make sense to rename the callback key to something else? (for example ack_callback). This would avoid any key conflicts if some other chain wants to use the callback key in the memo

@ValarDragon
Copy link
Member

oh rip I didn't realize all the lines were from cargo.lock, otherwise I would've reviewed sooner. Sorry 😅

@nicolaslara
Copy link
Contributor Author

Note to self: we probably want to remove the stored sequence=>contract mapping after the packet has been processed

@nicolaslara
Copy link
Contributor Author

E2E tests are fixed

x/ibc-hooks/README.md Outdated Show resolved Hide resolved
Comment on lines +355 to +369
// custom MsgTransfer constructor that supports Memo
func NewMsgTransfer(
token sdk.Coin, sender, receiver string, memo string,
) *transfertypes.MsgTransfer {
return &transfertypes.MsgTransfer{
SourcePort: "transfer",
SourceChannel: "channel-0",
Token: token,
Sender: sender,
Receiver: receiver,
TimeoutHeight: clienttypes.NewHeight(0, 100),
TimeoutTimestamp: 0,
Memo: memo,
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Would it be at all valuable to include a MsgTransfer without a memo, to ensure packet is still properly relayed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added a test for this. I'm still using NewMsgTransfer but with "" as the memo.

Copy link
Member

Choose a reason for hiding this comment

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

Sweet! Do you feel like using a message that has the field completely missing would potentially act differently than a blank string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it will, as getMemo() would still return the default "" if the memo isn't there.

x/ibc-hooks/types/keys.go Show resolved Hide resolved
Comment on lines 31 to 37
hooksKeeper *keeper.Keeper
}

func NewWasmHooks(contractKeeper *wasmkeeper.PermissionedKeeper) WasmHooks {
return WasmHooks{ContractKeeper: contractKeeper}
func NewWasmHooks(hooksKeeper *keeper.Keeper, contractKeeper *wasmkeeper.PermissionedKeeper) WasmHooks {
return WasmHooks{
ContractKeeper: contractKeeper,
hooksKeeper: hooksKeeper,
Copy link
Member

Choose a reason for hiding this comment

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

nit: maybe call this ibchooksKeeper to avoid confusion in the future

Copy link
Contributor Author

Choose a reason for hiding this comment

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

renamed

x/ibc-hooks/wasm_hook.go Outdated Show resolved Hide resolved
Comment on lines 259 to 268
packetWithoutMemo := channeltypes.Packet{
Sequence: concretePacket.Sequence,
SourcePort: concretePacket.SourcePort,
SourceChannel: concretePacket.SourceChannel,
DestinationPort: concretePacket.DestinationPort,
DestinationChannel: concretePacket.DestinationChannel,
Data: dataBytes,
TimeoutTimestamp: concretePacket.TimeoutTimestamp,
TimeoutHeight: concretePacket.TimeoutHeight,
}
Copy link
Member

Choose a reason for hiding this comment

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

Oh looks like you test without memo here, so potentially disregard previous message about testing this (unless this is testing a different use case)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is not part of the test. This is just recreating the packet but without the original memo. That's because we want to remove the callback key from the memo (as we've already "consumed" it).

x/ibc-hooks/README.md Outdated Show resolved Hide resolved
x/ibc-hooks/README.md Outdated Show resolved Hide resolved
Comment on lines +140 to +143

The wasm hooks will keep the mapping from the packet's channel and sequence to the contract in storage. When an ack is
received, it will notify the specified contract via a sudo message.
Copy link
Member

@ValarDragon ValarDragon Dec 15, 2022

Choose a reason for hiding this comment

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

Just realized, contract authors have to handle someone setting a callback the contract wasn't expecting. (So they need to do some validation)

Copy link
Member

Choose a reason for hiding this comment

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

Should we add a version field on ibc_callback or probably not needed? (e.g. ibc_callback_v1)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

someone setting a callback the contract wasn't expecting

Senders don't specify the callback format though. Just who will receive it. So as long as the format doesn't change on the chain, contracts should be able to trust the interface.

I like the idea of versioning when we need it. But since we don't have plans for v2 yet, I think the easiest thing here would be to leave it as is and then add ibc_callback_v2 if we make a change.

Comment on lines +235 to +239
// We remove the callback metadata from the memo as it has already been processed.

// If the only available key in the memo is the callback, we should remove the memo
// from the data completely so the packet is sent without it.
// This way receiver chains that are on old versions of IBC will be able to process the packet
Copy link
Member

Choose a reason for hiding this comment

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

Oh woah thats smart

return sdkerrors.Wrap(err, "Send packet with callback error")
}

packetWithoutMemo := channeltypes.Packet{
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
packetWithoutMemo := channeltypes.Packet{
packetWithoutCallbackMemo := channeltypes.Packet{

Still has memo if multiple keys!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

renamed

Copy link
Member

@ValarDragon ValarDragon left a comment

Choose a reason for hiding this comment

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

Nice job! This LGTM, only thing of concern is just that I need to double check how these override hooks mesh with anything else that may be on the stack, presumably they still run, by virtue of rate limiting tests still passing

nicolaslara and others added 3 commits December 16, 2022 09:50
Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
@nicolaslara
Copy link
Contributor Author

only thing of concern is just that I need to double check how these override hooks mesh with anything else that may be on the stack, presumably they still run, by virtue of rate limiting tests still passing

yeah, I checked that they would work with rate limiting, and tried to keep them as unintrusive as possible when designing them. AFAICT, they should be compatible with anything else we're accepting now (if you don't specify specific actions in the memo, they behave as normal). We may uncover some integration issues when adding the PacketForwardMiddleware, but the plan is they should seamlessly work together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C:app-wiring Changes to the app folder C:docs Improvements or additions to documentation T:build V:state/breaking State machine breaking PR
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

3 participants