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

Add standard acknowledgement to channels and apply usage in ibc transfer #7263

Merged
merged 19 commits into from
Sep 9, 2020
Merged
Show file tree
Hide file tree
Changes from 18 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
4 changes: 3 additions & 1 deletion docs/ibc/custom.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,9 @@ receive acknowledegments with the IBC modules as byte strings.

Thus, modules must agree on how to encode/decode acknowledgements. The process of creating an
acknowledgement struct along with encoding and decoding it, is very similar to the packet data
example above.
example above. [ICS 04](https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope)
specifies a recommended format for acknowledgements. This acknowledgement type can be imported from
[channel types](https://github.com/cosmos/cosmos-sdk/tree/master/x/ibc/04-channel/types).

#### Acknowledging Packets

Expand Down
31 changes: 23 additions & 8 deletions proto/ibc/channel/channel.proto
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ message MsgChannelOpenConfirm {
bytes signer = 5 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"];
}

// MsgChannelOpenConfirm defines a msg sent by a Relayer to Chain A
// MsgChannelCloseInit defines a msg sent by a Relayer to Chain A
// to close a channel with Chain B.
message MsgChannelCloseInit {
string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""];
Expand Down Expand Up @@ -114,8 +114,8 @@ message Channel {
Order ordering = 2;
// counterparty channel end
Counterparty counterparty = 3 [(gogoproto.nullable) = false];
// list of connection identifiers, in order, along which packets sent on this
// channel will travel
// list of connection identifiers, in order, along which packets sent on
// this channel will travel
repeated string connection_hops = 4 [(gogoproto.moretags) = "yaml:\"connection_hops\""];
// opaque channel version, which is agreed upon during the handshake
string version = 5;
Expand All @@ -132,8 +132,8 @@ message IdentifiedChannel {
Order ordering = 2;
// counterparty channel end
Counterparty counterparty = 3 [(gogoproto.nullable) = false];
// list of connection identifiers, in order, along which packets sent on this
// channel will travel
// list of connection identifiers, in order, along which packets sent on
// this channel will travel
repeated string connection_hops = 4 [(gogoproto.moretags) = "yaml:\"connection_hops\""];
// opaque channel version, which is agreed upon during the handshake
string version = 5;
Expand Down Expand Up @@ -189,9 +189,9 @@ message Counterparty {
message Packet {
option (gogoproto.goproto_getters) = false;

// number corresponds to the order of sends and receives, where a Packet with
// an earlier sequence number must be sent and received before a Packet with a
// later sequence number.
// number corresponds to the order of sends and receives, where a Packet
// with an earlier sequence number must be sent and received before a Packet
// with a later sequence number.
uint64 sequence = 1;
// identifies the port on the sending chain.
string source_port = 2 [(gogoproto.moretags) = "yaml:\"source_port\""];
Expand Down Expand Up @@ -223,3 +223,18 @@ message PacketAckCommitment {
// packet commitment hash.
bytes hash = 4;
}

// Acknowledgement is the recommended acknowledgement format to be used by
// app-specifc protocols.
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
// NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental
// conflicts with other protobuf message formats used for acknowledgements.
// The first byte of any message with this format will be the non-ASCII values
// `0xaa` (result) or `0xb2` (error). Implemented as defined by ICS:
// https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope
message Acknowledgement {
// response contains either a result or an error and must be non-empty
oneof response {
bytes result = 21;
string error = 22;
}
}
28 changes: 11 additions & 17 deletions proto/ibc/transfer/transfer.proto
Original file line number Diff line number Diff line change
Expand Up @@ -43,31 +43,25 @@ message FungibleTokenPacketData {
string receiver = 4;
}

// FungibleTokenPacketAcknowledgement contains a boolean success flag and an
// optional error msg error msg is empty string on success See spec for
// onAcknowledgePacket:
// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay
message FungibleTokenPacketAcknowledgement {
bool success = 1;
string error = 2;
}

// DenomTrace contains the base denomination for ICS20 fungible tokens and the source tracing
// information path.
// DenomTrace contains the base denomination for ICS20 fungible tokens and the
// source tracing information path.
message DenomTrace {
// path defines the chain of port/channel identifiers used for tracing the source of the fungible
// token.
// path defines the chain of port/channel identifiers used for tracing the
// source of the fungible token.
string path = 1;
// base denomination of the relayed fungible token.
string base_denom = 2;
}

// Params defines the set of IBC transfer parameters.
// NOTE: To prevent a single token from being transferred, set the TransfersEnabled parameter to
// true and then set the bank module's SendEnabled parameter for the denomination to false.
// NOTE: To prevent a single token from being transferred, set the
// TransfersEnabled parameter to true and then set the bank module's SendEnabled
// parameter for the denomination to false.
message Params {
// send_enabled enables or disables all cross-chain token transfers from this chain.
// send_enabled enables or disables all cross-chain token transfers from this
// chain.
bool send_enabled = 1 [(gogoproto.moretags) = "yaml:\"send_enabled\""];
// receive_enabled enables or disables all cross-chain token transfers to this chain.
// receive_enabled enables or disables all cross-chain token transfers to this
// chain.
bool receive_enabled = 2 [(gogoproto.moretags) = "yaml:\"receive_enabled\""];
}
2 changes: 1 addition & 1 deletion x/ibc-transfer/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() {
// relay send
fungibleTokenPacket := types.NewFungibleTokenPacketData(coinToSendToB.Denom, coinToSendToB.Amount.Uint64(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String())
packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0)
ack := types.FungibleTokenPacketAcknowledgement{Success: true}
ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)})
err = suite.coordinator.RelayPacket(suite.chainA, suite.chainB, clientA, clientB, packet, ack.GetBytes())
suite.Require().NoError(err) // relay committed

Expand Down
10 changes: 7 additions & 3 deletions x/ibc-transfer/keeper/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,15 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t
// acknowledgement written on the receiving chain. If the acknowledgement
// was a success then nothing occurs. If the acknowledgement failed, then
// the sender is refunded their tokens using the refundPacketToken function.
func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData, ack types.FungibleTokenPacketAcknowledgement) error {
if !ack.Success {
func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData, ack channeltypes.Acknowledgement) error {
switch ack.Response.(type) {
case *channeltypes.Acknowledgement_Error:
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we test this somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes I think it should be tested by the commented tests. I wanted to uncomment the tests in a followup pr to keep this pr lightweight. There is an issue for this #7261

return k.refundPacketToken(ctx, packet, data)
default:
// the acknowledgement succeeded on the receiving chain so nothing
// needs to be executed and no error needs to be returned
return nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit, can you add a note on why we return nil instead of an error?

}
return nil
}

// OnTimeoutPacket refunds the sender since the original packet sent was
Expand Down
13 changes: 4 additions & 9 deletions x/ibc-transfer/keeper/relay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
// relay send packet
fungibleTokenPacket := types.NewFungibleTokenPacketData(coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String())
packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, channelB.PortID, channelB.ID, channelA.PortID, channelA.ID, 110, 0)
ack := types.FungibleTokenPacketAcknowledgement{Success: true}
ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)})
err = suite.coordinator.RelayPacket(suite.chainB, suite.chainA, clientB, clientA, packet, ack.GetBytes())
suite.Require().NoError(err) // relay committed

Expand Down Expand Up @@ -229,21 +229,16 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
// to chainB.
func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() {
var (
successAck = types.FungibleTokenPacketAcknowledgement{
Success: true,
}
failedAck = types.FungibleTokenPacketAcknowledgement{
Success: false,
Error: "failed packet transfer",
}
successAck = channeltypes.NewResultAcknowledgement([]byte{byte(1)})
failedAck = channeltypes.NewErrorAcknowledgement("failed packet transfer")

channelA, channelB ibctesting.TestChannel
coins sdk.Coins
)

testCases := []struct {
msg string
ack types.FungibleTokenPacketAcknowledgement
ack channeltypes.Acknowledgement
malleate func()
source bool
success bool // success of ack
Expand Down
30 changes: 17 additions & 13 deletions x/ibc-transfer/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,16 +305,11 @@ func (am AppModule) OnRecvPacket(
return nil, nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error())
}

acknowledgement := types.FungibleTokenPacketAcknowledgement{
Success: true,
Error: "",
}
acknowledgement := channeltypes.NewResultAcknowledgement([]byte{byte(1)})
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 wasn't sure what to put here since it is not specified in ICS20 at the moment.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know either, but this seems like a fair non-empty success flag.
(Also 1 is a nice replacement for true and auto-convert in many languages)


if err := am.keeper.OnRecvPacket(ctx, packet, data); err != nil {
acknowledgement = types.FungibleTokenPacketAcknowledgement{
Success: false,
Error: err.Error(),
}
err := am.keeper.OnRecvPacket(ctx, packet, data)
if err != nil {
acknowledgement = channeltypes.NewErrorAcknowledgement(err.Error())
}

ctx.EventManager().EmitEvent(
Expand All @@ -324,6 +319,7 @@ func (am AppModule) OnRecvPacket(
sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver),
sdk.NewAttribute(types.AttributeKeyDenom, data.Denom),
sdk.NewAttribute(types.AttributeKeyAmount, fmt.Sprintf("%d", data.Amount)),
sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", err != nil)),
),
)

Expand All @@ -338,7 +334,7 @@ func (am AppModule) OnAcknowledgementPacket(
packet channeltypes.Packet,
acknowledgement []byte,
) (*sdk.Result, error) {
var ack types.FungibleTokenPacketAcknowledgement
var ack channeltypes.Acknowledgement
if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

shouldn't we use the cdc in the keeper?

Copy link
Collaborator

Choose a reason for hiding this comment

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

the keeper codec is a BinaryMarshaler, thus it doesn't support JSON unmarshal

return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err)
}
Expand All @@ -358,15 +354,23 @@ func (am AppModule) OnAcknowledgementPacket(
sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver),
sdk.NewAttribute(types.AttributeKeyDenom, data.Denom),
sdk.NewAttribute(types.AttributeKeyAmount, fmt.Sprintf("%d", data.Amount)),
sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", ack.Success)),
sdk.NewAttribute(types.AttributeKeyAck, fmt.Sprintf("%v", ack)),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

thoughts? or should I add a switch to check the type and emit a bool?

Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah we should

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 will have it emit the result or error message as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

instead of the bool

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated, let me know if you agree, with specs as well

Copy link
Contributor

Choose a reason for hiding this comment

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

I like the usage above:

sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", err != nil))

We could use %v instead (or as well), but it would be nice to have them both the same (the attributes in the two different functions)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah yea, I agree. Will update in a followup. We discussed standardizing IBC events on the IBC call which I think would be very useful in cases like this where there are multiple way to represent the same information

),
)

if !ack.Success {
switch resp := ack.Response.(type) {
case *channeltypes.Acknowledgement_Result:
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypePacket,
sdk.NewAttribute(types.AttributeKeyAckSuccess, string(resp.Result)),
),
)
case *channeltypes.Acknowledgement_Error:
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypePacket,
sdk.NewAttribute(types.AttributeKeyAckError, ack.Error),
sdk.NewAttribute(types.AttributeKeyAckError, resp.Error),
),
)
}
Expand Down
10 changes: 10 additions & 0 deletions x/ibc-transfer/spec/01_concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,13 @@ order: 1
-->

# Concepts

## Acknowledgements

ICS20 uses the recommended acknowledgement format as specified by [ICS 04](https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope).

A successful receive of a transfer packet will result in a Result Acknowledgement being written
with the value `[]byte(byte(1))` in the `Response` field.

An unsuccessful receive of a transfer packet will result in an Error Acknowledgement being written
with the error message in the `Response` field.
15 changes: 8 additions & 7 deletions x/ibc-transfer/spec/05_events.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@ order: 5
| fungible_token_packet | receiver | {receiver} |
| fungible_token_packet | denom | {denom} |
| fungible_token_packet | amount | {amount} |
| fungible_token_packet | success | {ackSuccess} |
| denomination_trace | trace_hash | {hex_hash} |

## OnAcknowledgePacket callback

| Type | Attribute Key | Attribute Value |
|-----------------------|---------------|-----------------|
| fungible_token_packet | module | transfer |
| fungible_token_packet | receiver | {receiver} |
| fungible_token_packet | denom | {denom} |
| fungible_token_packet | amount | {amount} |
| fungible_token_packet | success | {ackSuccess} |
| Type | Attribute Key | Attribute Value |
|-----------------------|-----------------|-------------------|
| fungible_token_packet | module | transfer |
| fungible_token_packet | receiver | {receiver} |
| fungible_token_packet | denom | {denom} |
| fungible_token_packet | amount | {amount} |
| fungible_token_packet | success | error | {ack.Response} |

## OnTimeoutPacket callback

Expand Down
1 change: 1 addition & 0 deletions x/ibc-transfer/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const (
AttributeKeyRefundDenom = "refund_denom"
AttributeKeyRefundAmount = "refund_amount"
AttributeKeyAckSuccess = "success"
AttributeKeyAck = "acknowledgement"
AttributeKeyAckError = "error"
AttributeKeyTraceHash = "trace_hash"
)
5 changes: 0 additions & 5 deletions x/ibc-transfer/types/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,3 @@ func (ftpd FungibleTokenPacketData) ValidateBasic() error {
func (ftpd FungibleTokenPacketData) GetBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&ftpd))
}

// GetBytes is a helper for serialising
func (ack FungibleTokenPacketAcknowledgement) GetBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&ack))
}
Loading