From 001b498151351f0afeaebd773cd3af2823196ceb Mon Sep 17 00:00:00 2001 From: Nicolas Lara Date: Mon, 10 Oct 2022 19:35:58 +0200 Subject: [PATCH] Added optional packet metadata to the packet and message types (#2305) * added optional packet metadata to the packet and message types * added docs * breaking the api (backports should add a utility function for this) * adding nil metadata on all the calls * added metadata to the cli * added events * breaking api for FungibleTokenPacketData * hex encoding metadata * added abstraction * fixed bad merge * added tests with metadata * added missing metadata to packet for recv * cleaning up metadata on every test * reset metadata * added metadata flag * lint * Update modules/apps/transfer/client/cli/tx.go Co-authored-by: Damian Nolan * fixed bad call in tests Co-authored-by: Damian Nolan (cherry picked from commit 82397d68ff9e11059e4baebb57fa14a73cedd5ed) # Conflicts: # docs/apps/transfer/messages.md # docs/ibc/proto-docs.md # go.mod # go.sum # modules/apps/29-fee/transfer_test.go # modules/apps/transfer/ibc_module.go # modules/apps/transfer/keeper/mbt_relay_test.go # modules/apps/transfer/keeper/relay_test.go # modules/apps/transfer/spec/05_events.md # modules/apps/transfer/types/packet.pb.go # modules/apps/transfer/types/tx.pb.go # proto/ibc/applications/interchain_accounts/controller/v1/tx.proto --- docs/apps/transfer/messages.md | 37 ++++++++ docs/ibc/proto-docs.md | 28 +++++++ go.mod | 4 + go.sum | 5 ++ modules/apps/29-fee/transfer_test.go | 70 ++++++++++++++++ modules/apps/transfer/client/cli/tx.go | 9 +- modules/apps/transfer/ibc_module.go | 20 +++++ .../apps/transfer/keeper/mbt_relay_test.go | 9 +- modules/apps/transfer/keeper/msg_server.go | 2 +- .../apps/transfer/keeper/msg_server_test.go | 1 + modules/apps/transfer/keeper/relay.go | 5 +- modules/apps/transfer/keeper/relay_test.go | 68 ++++++++++++++- modules/apps/transfer/spec/05_events.md | 7 ++ modules/apps/transfer/transfer_test.go | 6 +- modules/apps/transfer/types/events.go | 1 + modules/apps/transfer/types/msgs.go | 2 + modules/apps/transfer/types/msgs_test.go | 34 ++++---- modules/apps/transfer/types/packet.go | 2 + modules/apps/transfer/types/packet.pb.go | 75 +++++++++++++++++ modules/apps/transfer/types/packet_test.go | 19 +++-- modules/apps/transfer/types/tx.pb.go | 84 ++++++++++++++++++- .../controller/v1/tx.proto | 50 +++++++++++ proto/ibc/applications/transfer/v1/tx.proto | 2 + .../ibc/applications/transfer/v2/packet.proto | 2 + 24 files changed, 504 insertions(+), 38 deletions(-) create mode 100644 docs/apps/transfer/messages.md create mode 100644 modules/apps/29-fee/transfer_test.go create mode 100644 proto/ibc/applications/interchain_accounts/controller/v1/tx.proto diff --git a/docs/apps/transfer/messages.md b/docs/apps/transfer/messages.md new file mode 100644 index 00000000000..8ed89ab8e07 --- /dev/null +++ b/docs/apps/transfer/messages.md @@ -0,0 +1,37 @@ + + +# Messages + +## `MsgTransfer` + +A fungible token cross chain transfer is achieved by using the `MsgTransfer`: + +```go +type MsgTransfer struct { + SourcePort string + SourceChannel string + Token sdk.Coin + Sender string + Receiver string + TimeoutHeight ibcexported.Height + TimeoutTimestamp uint64 + Metadata []byte +} +``` + +This message is expected to fail if: + +- `SourcePort` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators). +- `SourceChannel` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators)). +- `Token` is invalid (denom is invalid or amount is negative) + - `Token.Amount` is not positive. + - `Token.Denom` is not a valid IBC denomination as per [ADR 001 - Coin Source Tracing](../../../docs/architecture/adr-001-coin-source-tracing.md). +- `Sender` is empty. +- `Receiver` is empty. +- `TimeoutHeight` and `TimeoutTimestamp` are both zero. + +This message will send a fungible token to the counterparty chain represented by the counterparty Channel End connected to the Channel End with the identifiers `SourcePort` and `SourceChannel`. + +The denomination provided for transfer should correspond to the same denomination represented on this chain. The prefixes will be added as necessary upon by the receiving chain. diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 773fa2ae36a..84ca5d26875 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -1097,6 +1097,7 @@ identifier fields. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | +<<<<<<< HEAD | `state` | [State](#ibc.core.channel.v1.State) | | current state of the channel end | | `ordering` | [Order](#ibc.core.channel.v1.Order) | | whether the channel is ordered or unordered | | `counterparty` | [Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end | @@ -1104,6 +1105,16 @@ identifier fields. | `version` | [string](#string) | | opaque channel version, which is agreed upon during the handshake | | `port_id` | [string](#string) | | port identifier | | `channel_id` | [string](#string) | | channel identifier | +======= +| `source_port` | [string](#string) | | the port on which the packet will be sent | +| `source_channel` | [string](#string) | | the channel by which the packet will be sent | +| `token` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | the tokens to be transferred | +| `sender` | [string](#string) | | the sender address | +| `receiver` | [string](#string) | | the recipient address on the destination chain | +| `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | Timeout height relative to the current block height. The timeout is disabled when set to 0. | +| `timeout_timestamp` | [uint64](#uint64) | | Timeout timestamp in absolute nanoseconds since unix epoch. The timeout is disabled when set to 0. | +| `metadata` | [bytes](#bytes) | | optional metadata | +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) @@ -1118,6 +1129,7 @@ Packet defines a type that carries data across different chains through IBC | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | +<<<<<<< HEAD | `sequence` | [uint64](#uint64) | | 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. | | `source_port` | [string](#string) | | identifies the port on the sending chain. | | `source_channel` | [string](#string) | | identifies the channel end on the sending chain. | @@ -1126,6 +1138,12 @@ Packet defines a type that carries data across different chains through IBC | `data` | [bytes](#bytes) | | actual opaque bytes transferred directly to the application module | | `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | block height after which the packet times out | | `timeout_timestamp` | [uint64](#uint64) | | block timestamp (in nanoseconds) after which the packet times out | +======= +| `sequence` | [uint64](#uint64) | | sequence number of the transfer packet sent | + + + +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) @@ -1166,6 +1184,16 @@ Order defines if a channel is ORDERED or UNORDERED | ORDER_UNORDERED | 1 | packets can be delivered in any order, which may differ from the order in which they were sent. | | ORDER_ORDERED | 2 | packets are delivered exactly in the order which they were sent | +<<<<<<< HEAD +======= +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `denom` | [string](#string) | | the token denomination to be transferred | +| `amount` | [string](#string) | | the token amount to be transferred | +| `sender` | [string](#string) | | the sender address | +| `receiver` | [string](#string) | | the recipient address on the destination chain | +| `metadata` | [bytes](#bytes) | | optional metadata | +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) diff --git a/go.mod b/go.mod index e731292fb90..246c5b2be4d 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,10 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/btcsuite/btcd v0.22.1 // indirect +<<<<<<< HEAD +======= + github.com/cenkalti/backoff/v4 v4.1.1 // indirect +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/coinbase/rosetta-sdk-go v0.7.0 // indirect diff --git a/go.sum b/go.sum index 910a1741860..b887674f278 100644 --- a/go.sum +++ b/go.sum @@ -130,6 +130,11 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +<<<<<<< HEAD +======= +github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= diff --git a/modules/apps/29-fee/transfer_test.go b/modules/apps/29-fee/transfer_test.go new file mode 100644 index 00000000000..b5c7a153ed9 --- /dev/null +++ b/modules/apps/29-fee/transfer_test.go @@ -0,0 +1,70 @@ +package fee_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v6/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v6/testing" +) + +// Integration test to ensure ics29 works with ics20 +func (suite *FeeTestSuite) TestFeeTransfer() { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + feeTransferVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.Version})) + path.EndpointA.ChannelConfig.Version = feeTransferVersion + path.EndpointB.ChannelConfig.Version = feeTransferVersion + path.EndpointA.ChannelConfig.PortID = transfertypes.PortID + path.EndpointB.ChannelConfig.PortID = transfertypes.PortID + + suite.coordinator.Setup(path) + + // set up coin & ics20 packet + coin := ibctesting.TestCoin + fee := types.Fee{ + RecvFee: defaultRecvFee, + AckFee: defaultAckFee, + TimeoutFee: defaultTimeoutFee, + } + + msgs := []sdk.Msg{ + types.NewMsgPayPacketFee(fee, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetAddress().String(), nil), + transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, nil), + } + res, err := suite.chainA.SendMsgs(msgs...) + suite.Require().NoError(err) // message committed + + // after incentivizing the packets + originalChainASenderAccountBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) + + packet, err := ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + + // register counterparty address on chainB + // relayerAddress is address of sender account on chainB, but we will use it on chainA + // to differentiate from the chainA.SenderAccount for checking successful relay payouts + relayerAddress := suite.chainB.SenderAccount.GetAddress() + + msgRegister := types.NewMsgRegisterCounterpartyPayee(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, suite.chainB.SenderAccount.GetAddress().String(), relayerAddress.String()) + _, err = suite.chainB.SendMsgs(msgRegister) + suite.Require().NoError(err) // message committed + + // relay packet + err = path.RelayPacket(packet) + suite.Require().NoError(err) // relay committed + + // ensure relayers got paid + // relayer for forward relay: chainB.SenderAccount + // relayer for reverse relay: chainA.SenderAccount + + // check forward relay balance + suite.Require().Equal( + fee.RecvFee, + sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainB.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)), + ) + + suite.Require().Equal( + fee.AckFee.Add(fee.TimeoutFee...), // ack fee paid, timeout fee refunded + sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)).Sub(originalChainASenderAccountBalance[0])) +} diff --git a/modules/apps/transfer/client/cli/tx.go b/modules/apps/transfer/client/cli/tx.go index 02e006be5eb..4c77c7b8cba 100644 --- a/modules/apps/transfer/client/cli/tx.go +++ b/modules/apps/transfer/client/cli/tx.go @@ -22,6 +22,7 @@ const ( flagPacketTimeoutHeight = "packet-timeout-height" flagPacketTimeoutTimestamp = "packet-timeout-timestamp" flagAbsoluteTimeouts = "absolute-timeouts" + flagMetadata = "metadata" ) // NewTransferTxCmd returns the command to create a NewMsgTransfer transaction @@ -76,6 +77,11 @@ corresponding to the counterparty channel. Any timeout set to 0 is disabled.`), return err } + metadataStr, err := cmd.Flags().GetString(flagMetadata) + if err != nil { + return err + } + // if the timeouts are not absolute, retrieve latest block height and block timestamp // for the consensus state connected to the destination port/channel if !absoluteTimeouts { @@ -111,7 +117,7 @@ corresponding to the counterparty channel. Any timeout set to 0 is disabled.`), } msg := types.NewMsgTransfer( - srcPort, srcChannel, coin, sender, receiver, timeoutHeight, timeoutTimestamp, + srcPort, srcChannel, coin, sender, receiver, timeoutHeight, timeoutTimestamp, []byte(metadataStr), ) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, @@ -120,6 +126,7 @@ corresponding to the counterparty channel. Any timeout set to 0 is disabled.`), cmd.Flags().String(flagPacketTimeoutHeight, types.DefaultRelativePacketTimeoutHeight, "Packet timeout block height. The timeout is disabled when set to 0-0.") cmd.Flags().Uint64(flagPacketTimeoutTimestamp, types.DefaultRelativePacketTimeoutTimestamp, "Packet timeout timestamp in nanoseconds from now. Default is 10 minutes. The timeout is disabled when set to 0.") cmd.Flags().Bool(flagAbsoluteTimeouts, false, "Timeout flags are used as absolute timeouts.") + cmd.Flags().String(flagMetadata, "", "Metadata to be sent along with the packet. The CLI accepts only strings here but you can construct a packet with arbitrary bytes via code.") flags.AddTxFlagsToCmd(cmd) return cmd diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index f611c62642f..634c34f27de 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -1,6 +1,7 @@ package transfer import ( + "encoding/hex" "fmt" "math" @@ -186,6 +187,23 @@ func (im IBCModule) OnRecvPacket( } } +<<<<<<< HEAD +======= + eventAttributes := []sdk.Attribute{ + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), + sdk.NewAttribute(types.AttributeKeyDenom, data.Denom), + sdk.NewAttribute(types.AttributeKeyAmount, data.Amount), + sdk.NewAttribute(types.AttributeKeyMetadata, hex.EncodeToString(data.Metadata)), + sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", ack.Success())), + } + + if ackErr != nil { + eventAttributes = append(eventAttributes, sdk.NewAttribute(types.AttributeKeyAckError, ackErr.Error())) + } + +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypePacket, @@ -230,6 +248,7 @@ func (im IBCModule) OnAcknowledgementPacket( sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), sdk.NewAttribute(types.AttributeKeyDenom, data.Denom), sdk.NewAttribute(types.AttributeKeyAmount, data.Amount), + sdk.NewAttribute(types.AttributeKeyMetadata, hex.EncodeToString(data.Metadata)), sdk.NewAttribute(types.AttributeKeyAck, ack.String()), ), ) @@ -276,6 +295,7 @@ func (im IBCModule) OnTimeoutPacket( sdk.NewAttribute(types.AttributeKeyRefundReceiver, data.Sender), sdk.NewAttribute(types.AttributeKeyRefundDenom, data.Denom), sdk.NewAttribute(types.AttributeKeyRefundAmount, data.Amount), + sdk.NewAttribute(types.AttributeKeyMetadata, hex.EncodeToString(data.Metadata)), ), ) diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index 59e4869e54e..824036e52cc 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -145,7 +145,8 @@ func FungibleTokenPacketFromTla(packet TlaFungibleTokenPacket) FungibleTokenPack DenomFromTla(packet.Data.Denom), packet.Data.Amount, AddressFromString(packet.Data.Sender), - AddressFromString(packet.Data.Receiver)), + AddressFromString(packet.Data.Receiver), + nil), } } @@ -344,8 +345,14 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { sdk.NewCoin(denom, amount), sender, tc.packet.Data.Receiver, +<<<<<<< HEAD clienttypes.NewHeight(0, 110), 0) +======= + clienttypes.NewHeight(1, 110), + 0, + nil) +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) } case "OnRecvPacket": err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, tc.packet.Data) diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index d9cd42cec38..ac4d9677cc8 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -21,7 +21,7 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. sequence, err := k.sendTransfer( ctx, msg.SourcePort, msg.SourceChannel, msg.Token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, - ) + msg.Metadata) if err != nil { return nil, err } diff --git a/modules/apps/transfer/keeper/msg_server_test.go b/modules/apps/transfer/keeper/msg_server_test.go index 89712e6737c..5de5f5a2ba8 100644 --- a/modules/apps/transfer/keeper/msg_server_test.go +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -54,6 +54,7 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainB.GetTimeoutHeight(), 0, // only use timeout height + []byte("custom metadata"), ) tc.malleate() diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 51e26d6f16d..921cefaf931 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -59,6 +59,7 @@ func (k Keeper) SendTransfer( receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, + metadata []byte, ) error { _, err := k.sendTransfer( ctx, @@ -69,6 +70,7 @@ func (k Keeper) SendTransfer( receiver, timeoutHeight, timeoutTimestamp, + metadata, ) return err } @@ -83,6 +85,7 @@ func (k Keeper) sendTransfer( receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, + metadata []byte, ) (uint64, error) { if !k.GetSendEnabled(ctx) { return 0, types.ErrSendDisabled @@ -173,7 +176,7 @@ func (k Keeper) sendTransfer( } packetData := types.NewFungibleTokenPacketData( - fullDenomPath, token.Amount.String(), sender.String(), receiver, + fullDenomPath, token.Amount.String(), sender.String(), receiver, metadata, ) packet := channeltypes.NewPacket( diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 789a61ecfa9..18bb6ae21d6 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -17,10 +17,11 @@ import ( // chainA and coin that orignate on chainB func (suite *KeeperTestSuite) TestSendTransfer() { var ( - amount sdk.Coin - path *ibctesting.Path - sender sdk.AccAddress - err error + amount sdk.Coin + path *ibctesting.Path + sender sdk.AccAddress + err error + metadata []byte ) testCases := []struct { @@ -36,6 +37,14 @@ func (suite *KeeperTestSuite) TestSendTransfer() { amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) }, true, true, }, + { + "successful transfer from source chain with metadata", + func() { + suite.coordinator.CreateTransferChannels(path) + amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + metadata = []byte("metadata") + }, true, true, + }, { "successful transfer with coin from counterparty chain", func() { @@ -44,6 +53,15 @@ func (suite *KeeperTestSuite) TestSendTransfer() { amount = types.GetTransferCoin(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom, sdk.NewInt(100)) }, false, true, }, + { + "successful transfer with coin from counterparty chain with metadata", + func() { + // send coin from chainA back to chainB + suite.coordinator.CreateTransferChannels(path) + amount = types.GetTransferCoin(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.DefaultBondDenom, sdk.NewInt(100)) + metadata = []byte("metadata") + }, false, true, + }, { "source channel not found", func() { @@ -114,19 +132,29 @@ func (suite *KeeperTestSuite) TestSendTransfer() { path = NewTransferPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) sender = suite.chainA.SenderAccount.GetAddress() + metadata = []byte{} tc.malleate() if !tc.sendFromSource { // send coin from chainB to chainA coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) +<<<<<<< HEAD transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0) +======= + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0, nil) +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) _, err = suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed // receive coin on chainA from chainB +<<<<<<< HEAD fungibleTokenPacket := types.NewFungibleTokenPacketData(coinFromBToA.Denom, coinFromBToA.Amount.String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, clienttypes.NewHeight(0, 110), 0) +======= + fungibleTokenPacket := types.NewFungibleTokenPacketData(coinFromBToA.Denom, coinFromBToA.Amount.String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), nil) + packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, clienttypes.NewHeight(1, 110), 0) +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) // get proof of packet commitment from chainB err = path.EndpointA.UpdateClient() @@ -142,6 +170,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() { err = suite.chainA.GetSimApp().TransferKeeper.SendTransfer( suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, amount, sender, suite.chainB.SenderAccount.GetAddress().String(), suite.chainB.GetTimeoutHeight(), 0, + metadata, ) if tc.expPass { @@ -162,6 +191,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { trace types.DenomTrace amount sdk.Int receiver string + metadata []byte ) testCases := []struct { @@ -171,7 +201,13 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { expPass bool }{ {"success receive on source chain", func() {}, true, true}, + {"success receive on source chain with metadata", func() { + metadata = []byte("metadata") + }, true, true}, {"success receive with coin from another chain as source", func() {}, false, true}, + {"success receive with coin from another chain as source with metadata", func() { + metadata = []byte("metadata") + }, false, true}, {"empty coin", func() { trace = types.DenomTrace{} amount = sdk.ZeroInt() @@ -212,13 +248,18 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { suite.coordinator.Setup(path) receiver = suite.chainB.SenderAccount.GetAddress().String() // must be explicitly changed in malleate + metadata = []byte{} // can be explicitly changed in malleate amount = sdk.NewInt(100) // must be explicitly changed in malleate seq := uint64(1) if tc.recvIsSource { // send coin from chainB to chainA, receive them, acknowledge them, and send back to chainB coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) +<<<<<<< HEAD transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0) +======= + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0, metadata) +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) res, err := suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed @@ -237,14 +278,23 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { } // send coin from chainA to chainB +<<<<<<< HEAD transferMsg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoin(trace.IBCDenom(), amount), suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(0, 110), 0) +======= + transferMsg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoin(trace.IBCDenom(), amount), suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(1, 110), 0, metadata) +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) _, err := suite.chainA.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed tc.malleate() +<<<<<<< HEAD data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), receiver) packet := channeltypes.NewPacket(data.GetBytes(), seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) +======= + data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), receiver, metadata) + packet := channeltypes.NewPacket(data.GetBytes(), seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, data) @@ -316,8 +366,13 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { tc.malleate() +<<<<<<< HEAD data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String()) packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) +======= + data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), nil) + packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) preCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) @@ -412,8 +467,13 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { tc.malleate() +<<<<<<< HEAD data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), sender, suite.chainB.SenderAccount.GetAddress().String()) packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) +======= + data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), sender, suite.chainB.SenderAccount.GetAddress().String(), nil) + packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) preCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) diff --git a/modules/apps/transfer/spec/05_events.md b/modules/apps/transfer/spec/05_events.md index 51b49da4602..c20b3f01c78 100644 --- a/modules/apps/transfer/spec/05_events.md +++ b/modules/apps/transfer/spec/05_events.md @@ -22,6 +22,7 @@ order: 5 | fungible_token_packet | denom | {denom} | | fungible_token_packet | amount | {amount} | | fungible_token_packet | success | {ackSuccess} | +| fungible_token_packet | metadata | {metadata} | | denomination_trace | trace_hash | {hex_hash} | ## OnAcknowledgePacket callback @@ -32,6 +33,11 @@ order: 5 | fungible_token_packet | receiver | {receiver} | | fungible_token_packet | denom | {denom} | | fungible_token_packet | amount | {amount} | +<<<<<<< HEAD:modules/apps/transfer/spec/05_events.md +======= +| fungible_token_packet | metadata | {metadata} | +| fungible_token_packet | acknowledgement | {ack.String()} | +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)):docs/apps/transfer/events.md | fungible_token_packet | success | error | {ack.Response} | ## OnTimeoutPacket callback @@ -42,3 +48,4 @@ order: 5 | fungible_token_packet | refund_receiver | {receiver} | | fungible_token_packet | denom | {denom} | | fungible_token_packet | amount | {amount} | +| fungible_token_packet | metadata | {metadata} | diff --git a/modules/apps/transfer/transfer_test.go b/modules/apps/transfer/transfer_test.go index 5190cdc8d29..1a72c23382d 100644 --- a/modules/apps/transfer/transfer_test.go +++ b/modules/apps/transfer/transfer_test.go @@ -54,7 +54,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { coinToSendToB := sdk.NewCoin(sdk.DefaultBondDenom, amount) // send from chainA to chainB - msg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0) + msg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, nil) res, err := suite.chainA.SendMsgs(msg) suite.Require().NoError(err) // message committed @@ -80,7 +80,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { suite.coordinator.Setup(pathBtoC) // send from chainB to chainC - msg = types.NewMsgTransfer(pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, coinSentFromAToB, suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0) + msg = types.NewMsgTransfer(pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, coinSentFromAToB, suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0, nil) res, err = suite.chainB.SendMsgs(msg) suite.Require().NoError(err) // message committed @@ -104,7 +104,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { suite.Require().Zero(balance.Amount.Int64()) // send from chainC back to chainB - msg = types.NewMsgTransfer(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, coinSentFromBToC, suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0) + msg = types.NewMsgTransfer(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, coinSentFromBToC, suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, nil) res, err = suite.chainC.SendMsgs(msg) suite.Require().NoError(err) // message committed diff --git a/modules/apps/transfer/types/events.go b/modules/apps/transfer/types/events.go index a3ed5b413c7..5cc87f11797 100644 --- a/modules/apps/transfer/types/events.go +++ b/modules/apps/transfer/types/events.go @@ -18,4 +18,5 @@ const ( AttributeKeyAck = "acknowledgement" AttributeKeyAckError = "error" AttributeKeyTraceHash = "trace_hash" + AttributeKeyMetadata = "metadata" ) diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index 13c0b8b419e..cd4e3f14dbf 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -22,6 +22,7 @@ func NewMsgTransfer( sourcePort, sourceChannel string, token sdk.Coin, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, + metadata []byte, ) *MsgTransfer { return &MsgTransfer{ SourcePort: sourcePort, @@ -31,6 +32,7 @@ func NewMsgTransfer( Receiver: receiver, TimeoutHeight: timeoutHeight, TimeoutTimestamp: timeoutTimestamp, + Metadata: metadata, } } diff --git a/modules/apps/transfer/types/msgs_test.go b/modules/apps/transfer/types/msgs_test.go index 00570ac15ed..c5b9d954a6b 100644 --- a/modules/apps/transfer/types/msgs_test.go +++ b/modules/apps/transfer/types/msgs_test.go @@ -41,7 +41,7 @@ var ( // TestMsgTransferRoute tests Route for MsgTransfer func TestMsgTransferRoute(t *testing.T) { - msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0) + msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, nil) require.Equal(t, RouterKey, msg.Route()) } @@ -54,7 +54,7 @@ func TestMsgTransferType(t *testing.T) { } func TestMsgTransferGetSignBytes(t *testing.T) { - msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0) + msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, nil) expected := fmt.Sprintf(`{"type":"cosmos-sdk/MsgTransfer","value":{"receiver":"%s","sender":"%s","source_channel":"testchannel","source_port":"testportid","timeout_height":{"revision_height":"10"},"token":{"amount":"100","denom":"atom"}}}`, addr2, addr1) require.NotPanics(t, func() { res := msg.GetSignBytes() @@ -69,20 +69,20 @@ func TestMsgTransferValidation(t *testing.T) { msg *MsgTransfer expPass bool }{ - {"valid msg with base denom", NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), true}, - {"valid msg with trace hash", NewMsgTransfer(validPort, validChannel, ibcCoin, addr1, addr2, timeoutHeight, 0), true}, - {"invalid ibc denom", NewMsgTransfer(validPort, validChannel, invalidIBCCoin, addr1, addr2, timeoutHeight, 0), false}, - {"too short port id", NewMsgTransfer(invalidShortPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"too long port id", NewMsgTransfer(invalidLongPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"port id contains non-alpha", NewMsgTransfer(invalidPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"too short channel id", NewMsgTransfer(validPort, invalidShortChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"too long channel id", NewMsgTransfer(validPort, invalidLongChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"channel id contains non-alpha", NewMsgTransfer(validPort, invalidChannel, coin, addr1, addr2, timeoutHeight, 0), false}, - {"invalid denom", NewMsgTransfer(validPort, validChannel, invalidDenomCoin, addr1, addr2, timeoutHeight, 0), false}, - {"zero coin", NewMsgTransfer(validPort, validChannel, zeroCoin, addr1, addr2, timeoutHeight, 0), false}, - {"missing sender address", NewMsgTransfer(validPort, validChannel, coin, emptyAddr, addr2, timeoutHeight, 0), false}, - {"missing recipient address", NewMsgTransfer(validPort, validChannel, coin, addr1, "", timeoutHeight, 0), false}, - {"empty coin", NewMsgTransfer(validPort, validChannel, sdk.Coin{}, addr1, addr2, timeoutHeight, 0), false}, + {"valid msg with base denom", NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, nil), true}, + {"valid msg with trace hash", NewMsgTransfer(validPort, validChannel, ibcCoin, addr1, addr2, timeoutHeight, 0, nil), true}, + {"invalid ibc denom", NewMsgTransfer(validPort, validChannel, invalidIBCCoin, addr1, addr2, timeoutHeight, 0, nil), false}, + {"too short port id", NewMsgTransfer(invalidShortPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, nil), false}, + {"too long port id", NewMsgTransfer(invalidLongPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, nil), false}, + {"port id contains non-alpha", NewMsgTransfer(invalidPort, validChannel, coin, addr1, addr2, timeoutHeight, 0, nil), false}, + {"too short channel id", NewMsgTransfer(validPort, invalidShortChannel, coin, addr1, addr2, timeoutHeight, 0, nil), false}, + {"too long channel id", NewMsgTransfer(validPort, invalidLongChannel, coin, addr1, addr2, timeoutHeight, 0, nil), false}, + {"channel id contains non-alpha", NewMsgTransfer(validPort, invalidChannel, coin, addr1, addr2, timeoutHeight, 0, nil), false}, + {"invalid denom", NewMsgTransfer(validPort, validChannel, invalidDenomCoin, addr1, addr2, timeoutHeight, 0, nil), false}, + {"zero coin", NewMsgTransfer(validPort, validChannel, zeroCoin, addr1, addr2, timeoutHeight, 0, nil), false}, + {"missing sender address", NewMsgTransfer(validPort, validChannel, coin, emptyAddr, addr2, timeoutHeight, 0, nil), false}, + {"missing recipient address", NewMsgTransfer(validPort, validChannel, coin, addr1, "", timeoutHeight, 0, nil), false}, + {"empty coin", NewMsgTransfer(validPort, validChannel, sdk.Coin{}, addr1, addr2, timeoutHeight, 0, nil), false}, } for i, tc := range testCases { @@ -99,7 +99,7 @@ func TestMsgTransferValidation(t *testing.T) { func TestMsgTransferGetSigners(t *testing.T) { addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - msg := NewMsgTransfer(validPort, validChannel, coin, addr.String(), addr2, timeoutHeight, 0) + msg := NewMsgTransfer(validPort, validChannel, coin, addr.String(), addr2, timeoutHeight, 0, nil) res := msg.GetSigners() require.Equal(t, []sdk.AccAddress{addr}, res) diff --git a/modules/apps/transfer/types/packet.go b/modules/apps/transfer/types/packet.go index a7384c47486..904f77c4e5d 100644 --- a/modules/apps/transfer/types/packet.go +++ b/modules/apps/transfer/types/packet.go @@ -25,12 +25,14 @@ var ( func NewFungibleTokenPacketData( denom string, amount string, sender, receiver string, + metadata []byte, ) FungibleTokenPacketData { return FungibleTokenPacketData{ Denom: denom, Amount: amount, Sender: sender, Receiver: receiver, + Metadata: metadata, } } diff --git a/modules/apps/transfer/types/packet.pb.go b/modules/apps/transfer/types/packet.pb.go index 3ebd1106f66..639c02680e2 100644 --- a/modules/apps/transfer/types/packet.pb.go +++ b/modules/apps/transfer/types/packet.pb.go @@ -34,6 +34,8 @@ type FungibleTokenPacketData struct { Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"` // the recipient address on the destination chain Receiver string `protobuf:"bytes,4,opt,name=receiver,proto3" json:"receiver,omitempty"` + // optional metadata + Metadata []byte `protobuf:"bytes,5,opt,name=metadata,proto3" json:"metadata,omitempty"` } func (m *FungibleTokenPacketData) Reset() { *m = FungibleTokenPacketData{} } @@ -97,6 +99,13 @@ func (m *FungibleTokenPacketData) GetReceiver() string { return "" } +func (m *FungibleTokenPacketData) GetMetadata() []byte { + if m != nil { + return m.Metadata + } + return nil +} + func init() { proto.RegisterType((*FungibleTokenPacketData)(nil), "ibc.applications.transfer.v2.FungibleTokenPacketData") } @@ -106,6 +115,7 @@ func init() { } var fileDescriptor_653ca2ce9a5ca313 = []byte{ +<<<<<<< HEAD // 242 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8f, 0xbd, 0x4a, 0x04, 0x31, 0x14, 0x46, 0x27, 0xfe, 0x2c, 0x9a, 0x72, 0x10, 0x1d, 0x44, 0x82, 0x58, 0x69, 0x61, 0x02, 0xbb, @@ -123,6 +133,26 @@ var fileDescriptor_653ca2ce9a5ca313 = []byte{ 0x37, 0x28, 0xfa, 0x85, 0xb0, 0x58, 0x77, 0x2d, 0xc4, 0x75, 0xff, 0x56, 0x77, 0x7a, 0xf7, 0x10, 0xd5, 0x2c, 0x47, 0x2f, 0x7e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x82, 0x62, 0x02, 0xc6, 0x21, 0x01, 0x00, 0x00, +======= + // 259 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0xb1, 0x4a, 0xc4, 0x40, + 0x10, 0x86, 0xb3, 0xea, 0x1d, 0x1a, 0xac, 0x82, 0x68, 0x10, 0x59, 0x0e, 0xab, 0xb3, 0x30, 0x0b, + 0x27, 0x68, 0x2f, 0x62, 0xad, 0x87, 0x95, 0xdd, 0xee, 0x66, 0x8c, 0xcb, 0x65, 0x77, 0xc2, 0xee, + 0x24, 0xe0, 0x5b, 0xd8, 0xf9, 0x4a, 0x96, 0x57, 0x5a, 0x4a, 0xf2, 0x22, 0x92, 0xc4, 0x3b, 0xae, + 0xfc, 0xbe, 0xf9, 0xa7, 0xf9, 0xe2, 0x2b, 0xa3, 0xb4, 0x90, 0x55, 0x55, 0x1a, 0x2d, 0xc9, 0xa0, + 0x0b, 0x82, 0xbc, 0x74, 0xe1, 0x0d, 0xbc, 0x68, 0x16, 0xa2, 0x92, 0x7a, 0x05, 0x94, 0x55, 0x1e, + 0x09, 0x93, 0x0b, 0xa3, 0x74, 0xb6, 0x3b, 0xcd, 0x36, 0xd3, 0xac, 0x59, 0x5c, 0x7e, 0xb1, 0xf8, + 0xec, 0xb1, 0x76, 0x85, 0x51, 0x25, 0xbc, 0xe0, 0x0a, 0xdc, 0xd3, 0xf0, 0xfb, 0x20, 0x49, 0x26, + 0x27, 0xf1, 0x24, 0x07, 0x87, 0x36, 0x65, 0x33, 0x36, 0x3f, 0x5a, 0x8e, 0x90, 0x9c, 0xc6, 0x53, + 0x69, 0xb1, 0x76, 0x94, 0xee, 0x0d, 0xfa, 0x9f, 0x7a, 0x1f, 0xc0, 0xe5, 0xe0, 0xd3, 0xfd, 0xd1, + 0x8f, 0x94, 0x9c, 0xc7, 0x87, 0x1e, 0x34, 0x98, 0x06, 0x7c, 0x7a, 0x30, 0x5c, 0xb6, 0xdc, 0xdf, + 0x2c, 0x90, 0xcc, 0x25, 0xc9, 0x74, 0x32, 0x63, 0xf3, 0xe3, 0xe5, 0x96, 0xef, 0x9f, 0xbf, 0x5b, + 0xce, 0xd6, 0x2d, 0x67, 0xbf, 0x2d, 0x67, 0x9f, 0x1d, 0x8f, 0xd6, 0x1d, 0x8f, 0x7e, 0x3a, 0x1e, + 0xbd, 0xde, 0x15, 0x86, 0xde, 0x6b, 0x95, 0x69, 0xb4, 0x42, 0x63, 0xb0, 0x18, 0x84, 0x51, 0xfa, + 0xba, 0x40, 0xd1, 0xdc, 0x0a, 0x8b, 0x79, 0x5d, 0x42, 0xe8, 0xe3, 0xec, 0x44, 0xa1, 0x8f, 0x0a, + 0x82, 0x9a, 0x0e, 0x45, 0x6e, 0xfe, 0x02, 0x00, 0x00, 0xff, 0xff, 0xe2, 0x6b, 0x46, 0x44, 0x3e, + 0x01, 0x00, 0x00, +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) } func (m *FungibleTokenPacketData) Marshal() (dAtA []byte, err error) { @@ -145,6 +175,13 @@ func (m *FungibleTokenPacketData) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if len(m.Metadata) > 0 { + i -= len(m.Metadata) + copy(dAtA[i:], m.Metadata) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Metadata))) + i-- + dAtA[i] = 0x2a + } if len(m.Receiver) > 0 { i -= len(m.Receiver) copy(dAtA[i:], m.Receiver) @@ -209,6 +246,10 @@ func (m *FungibleTokenPacketData) Size() (n int) { if l > 0 { n += 1 + l + sovPacket(uint64(l)) } + l = len(m.Metadata) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } return n } @@ -375,6 +416,40 @@ func (m *FungibleTokenPacketData) Unmarshal(dAtA []byte) error { } m.Receiver = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Metadata = append(m.Metadata[:0], dAtA[iNdEx:postIndex]...) + if m.Metadata == nil { + m.Metadata = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPacket(dAtA[iNdEx:]) diff --git a/modules/apps/transfer/types/packet_test.go b/modules/apps/transfer/types/packet_test.go index e5d21d648d2..a2f8cfc6e00 100644 --- a/modules/apps/transfer/types/packet_test.go +++ b/modules/apps/transfer/types/packet_test.go @@ -20,15 +20,16 @@ func TestFungibleTokenPacketDataValidateBasic(t *testing.T) { packetData FungibleTokenPacketData expPass bool }{ - {"valid packet", NewFungibleTokenPacketData(denom, amount, addr1, addr2), true}, - {"valid packet with large amount", NewFungibleTokenPacketData(denom, largeAmount, addr1, addr2), true}, - {"invalid denom", NewFungibleTokenPacketData("", amount, addr1, addr2), false}, - {"invalid empty amount", NewFungibleTokenPacketData(denom, "", addr1, addr2), false}, - {"invalid zero amount", NewFungibleTokenPacketData(denom, "0", addr1, addr2), false}, - {"invalid negative amount", NewFungibleTokenPacketData(denom, "-1", addr1, addr2), false}, - {"invalid large amount", NewFungibleTokenPacketData(denom, invalidLargeAmount, addr1, addr2), false}, - {"missing sender address", NewFungibleTokenPacketData(denom, amount, emptyAddr, addr2), false}, - {"missing recipient address", NewFungibleTokenPacketData(denom, amount, addr1, emptyAddr), false}, + {"valid packet", NewFungibleTokenPacketData(denom, amount, addr1, addr2, nil), true}, + {"valid packet with metadata", NewFungibleTokenPacketData(denom, amount, addr1, addr2, []byte("metadata")), true}, + {"valid packet with large amount", NewFungibleTokenPacketData(denom, largeAmount, addr1, addr2, nil), true}, + {"invalid denom", NewFungibleTokenPacketData("", amount, addr1, addr2, nil), false}, + {"invalid empty amount", NewFungibleTokenPacketData(denom, "", addr1, addr2, nil), false}, + {"invalid zero amount", NewFungibleTokenPacketData(denom, "0", addr1, addr2, nil), false}, + {"invalid negative amount", NewFungibleTokenPacketData(denom, "-1", addr1, addr2, nil), false}, + {"invalid large amount", NewFungibleTokenPacketData(denom, invalidLargeAmount, addr1, addr2, nil), false}, + {"missing sender address", NewFungibleTokenPacketData(denom, amount, emptyAddr, addr2, nil), false}, + {"missing recipient address", NewFungibleTokenPacketData(denom, amount, addr1, emptyAddr, nil), false}, } for i, tc := range testCases { diff --git a/modules/apps/transfer/types/tx.pb.go b/modules/apps/transfer/types/tx.pb.go index 608960e63bb..ef5862b4e8e 100644 --- a/modules/apps/transfer/types/tx.pb.go +++ b/modules/apps/transfer/types/tx.pb.go @@ -50,6 +50,8 @@ type MsgTransfer struct { // Timeout timestamp in absolute nanoseconds since unix epoch. // The timeout is disabled when set to 0. TimeoutTimestamp uint64 `protobuf:"varint,7,opt,name=timeout_timestamp,json=timeoutTimestamp,proto3" json:"timeout_timestamp,omitempty" yaml:"timeout_timestamp"` + // optional metadata + Metadata []byte `protobuf:"bytes,8,opt,name=metadata,proto3" json:"metadata,omitempty"` } func (m *MsgTransfer) Reset() { *m = MsgTransfer{} } @@ -141,8 +143,9 @@ func init() { } var fileDescriptor_7401ed9bed2f8e09 = []byte{ - // 506 bytes of a gzipped FileDescriptorProto + // 521 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x31, 0x6f, 0xd3, 0x40, +<<<<<<< HEAD 0x14, 0xc7, 0x6d, 0x92, 0x86, 0x70, 0x51, 0x2b, 0x30, 0x50, 0xb9, 0x51, 0xb1, 0x23, 0x4b, 0x48, 0x61, 0xe0, 0x4e, 0x6e, 0x85, 0x2a, 0x75, 0x42, 0xe9, 0x02, 0x43, 0x25, 0xb0, 0x3a, 0xb1, 0x14, 0xfb, 0xfa, 0x70, 0x4e, 0xc4, 0xf7, 0x8c, 0xef, 0x62, 0xd1, 0x6f, 0xc0, 0xc8, 0x47, 0xe8, 0xcc, @@ -174,6 +177,40 @@ var fileDescriptor_7401ed9bed2f8e09 = []byte{ 0x6b, 0xd6, 0x56, 0x24, 0xfc, 0x65, 0x8a, 0xac, 0x3c, 0x66, 0x19, 0x5e, 0x4d, 0x27, 0xa0, 0xcc, 0x33, 0xd9, 0x78, 0x1e, 0xfa, 0x3a, 0x07, 0x95, 0x74, 0xaa, 0x55, 0x3d, 0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, 0x6a, 0xc3, 0xd3, 0xe1, 0x48, 0x03, 0x00, 0x00, +======= + 0x14, 0xc7, 0x6d, 0x92, 0x86, 0x70, 0xa1, 0x15, 0x18, 0xa8, 0xdc, 0xa8, 0xd8, 0x91, 0x25, 0xa4, + 0x30, 0x70, 0x27, 0x17, 0x41, 0xa5, 0x4e, 0x28, 0x5d, 0x60, 0xa8, 0x04, 0x56, 0x27, 0x96, 0x72, + 0xbe, 0x3c, 0x9c, 0x13, 0xf1, 0x9d, 0xf1, 0x5d, 0x2c, 0xfa, 0x0d, 0x18, 0xf9, 0x08, 0xfd, 0x34, + 0xa8, 0x63, 0x47, 0xa6, 0x08, 0x25, 0x0b, 0x73, 0x3e, 0x01, 0x3a, 0xdb, 0x09, 0xce, 0x82, 0x3a, + 0xe5, 0xfe, 0xef, 0xfd, 0x5e, 0xfe, 0x7e, 0xf7, 0xde, 0xa1, 0x67, 0x3c, 0x66, 0x84, 0x66, 0xd9, + 0x94, 0x33, 0xaa, 0xb9, 0x14, 0x8a, 0xe8, 0x9c, 0x0a, 0xf5, 0x19, 0x72, 0x52, 0x84, 0x44, 0x7f, + 0xc3, 0x59, 0x2e, 0xb5, 0x74, 0x0e, 0x79, 0xcc, 0x70, 0x13, 0xc3, 0x6b, 0x0c, 0x17, 0x61, 0xff, + 0x71, 0x22, 0x13, 0x59, 0x82, 0xc4, 0x9c, 0xaa, 0x9a, 0xbe, 0xc7, 0xa4, 0x4a, 0xa5, 0x22, 0x31, + 0x55, 0x40, 0x8a, 0x30, 0x06, 0x4d, 0x43, 0xc2, 0x24, 0x17, 0x75, 0xde, 0x37, 0xd6, 0x4c, 0xe6, + 0x40, 0xd8, 0x94, 0x83, 0xd0, 0xc6, 0xb0, 0x3a, 0x55, 0x40, 0xf0, 0xb3, 0x85, 0x7a, 0x67, 0x2a, + 0x39, 0xaf, 0x9d, 0x9c, 0x63, 0xd4, 0x53, 0x72, 0x96, 0x33, 0xb8, 0xc8, 0x64, 0xae, 0x5d, 0x7b, + 0x60, 0x0f, 0xef, 0x8d, 0xf6, 0x57, 0x73, 0xdf, 0xb9, 0xa4, 0xe9, 0xf4, 0x24, 0x68, 0x24, 0x83, + 0x08, 0x55, 0xea, 0xbd, 0xcc, 0xb5, 0xf3, 0x06, 0xed, 0xd5, 0x39, 0x36, 0xa1, 0x42, 0xc0, 0xd4, + 0xbd, 0x53, 0xd6, 0x1e, 0xac, 0xe6, 0xfe, 0x93, 0xad, 0xda, 0x3a, 0x1f, 0x44, 0xbb, 0x55, 0xe0, + 0xb4, 0xd2, 0xce, 0x2b, 0xb4, 0xa3, 0xe5, 0x17, 0x10, 0x6e, 0x6b, 0x60, 0x0f, 0x7b, 0x47, 0x07, + 0xb8, 0xea, 0x0d, 0x9b, 0xde, 0x70, 0xdd, 0x1b, 0x3e, 0x95, 0x5c, 0x8c, 0xda, 0xd7, 0x73, 0xdf, + 0x8a, 0x2a, 0xda, 0xd9, 0x47, 0x1d, 0x05, 0x62, 0x0c, 0xb9, 0xdb, 0x36, 0x86, 0x51, 0xad, 0x9c, + 0x3e, 0xea, 0xe6, 0xc0, 0x80, 0x17, 0x90, 0xbb, 0x3b, 0x65, 0x66, 0xa3, 0x9d, 0x4f, 0x68, 0x4f, + 0xf3, 0x14, 0xe4, 0x4c, 0x5f, 0x4c, 0x80, 0x27, 0x13, 0xed, 0x76, 0x4a, 0xcf, 0x3e, 0x36, 0x33, + 0x30, 0xf7, 0x85, 0xeb, 0x5b, 0x2a, 0x42, 0xfc, 0xb6, 0x24, 0x46, 0x4f, 0x8d, 0xe9, 0xbf, 0x66, + 0xb6, 0xeb, 0x83, 0x68, 0xb7, 0x0e, 0x54, 0xb4, 0xf3, 0x0e, 0x3d, 0x5c, 0x13, 0xe6, 0x57, 0x69, + 0x9a, 0x66, 0xee, 0xdd, 0x81, 0x3d, 0x6c, 0x8f, 0x0e, 0x57, 0x73, 0xdf, 0xdd, 0xfe, 0x93, 0x0d, + 0x12, 0x44, 0x0f, 0xea, 0xd8, 0xf9, 0x3a, 0x64, 0x1a, 0x49, 0x41, 0xd3, 0x31, 0xd5, 0xd4, 0xed, + 0x0e, 0xec, 0xe1, 0xfd, 0x68, 0xa3, 0x4f, 0xba, 0xdf, 0xaf, 0x7c, 0xeb, 0xcf, 0x95, 0x6f, 0x05, + 0x21, 0x7a, 0xd4, 0x98, 0x63, 0x04, 0x2a, 0x93, 0x42, 0x81, 0x29, 0x56, 0xf0, 0x75, 0x06, 0x82, + 0x41, 0x39, 0xcc, 0x76, 0xb4, 0xd1, 0x47, 0x12, 0xb5, 0xce, 0x54, 0xe2, 0x4c, 0x50, 0x77, 0x33, + 0xfe, 0xe7, 0xf8, 0x7f, 0x4b, 0x88, 0x1b, 0x0e, 0xfd, 0xf0, 0xd6, 0xe8, 0xfa, 0x63, 0x46, 0x1f, + 0xae, 0x17, 0x9e, 0x7d, 0xb3, 0xf0, 0xec, 0xdf, 0x0b, 0xcf, 0xfe, 0xb1, 0xf4, 0xac, 0x9b, 0xa5, + 0x67, 0xfd, 0x5a, 0x7a, 0xd6, 0xc7, 0xe3, 0x84, 0xeb, 0xc9, 0x2c, 0xc6, 0x4c, 0xa6, 0xa4, 0x5e, + 0x69, 0x1e, 0xb3, 0x17, 0x89, 0x24, 0xc5, 0x6b, 0x92, 0xca, 0xf1, 0x6c, 0x0a, 0xca, 0x3c, 0xa1, + 0xc6, 0xd3, 0xd1, 0x97, 0x19, 0xa8, 0xb8, 0x53, 0xae, 0xf1, 0xcb, 0xbf, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xc0, 0x4c, 0xff, 0xf5, 0x64, 0x03, 0x00, 0x00, +>>>>>>> 82397d6 (Added optional packet metadata to the packet and message types (#2305)) } // Reference imports to suppress errors if they are not otherwise used. @@ -278,6 +315,13 @@ func (m *MsgTransfer) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Metadata) > 0 { + i -= len(m.Metadata) + copy(dAtA[i:], m.Metadata) + i = encodeVarintTx(dAtA, i, uint64(len(m.Metadata))) + i-- + dAtA[i] = 0x42 + } if m.TimeoutTimestamp != 0 { i = encodeVarintTx(dAtA, i, uint64(m.TimeoutTimestamp)) i-- @@ -402,6 +446,10 @@ func (m *MsgTransfer) Size() (n int) { if m.TimeoutTimestamp != 0 { n += 1 + sovTx(uint64(m.TimeoutTimestamp)) } + l = len(m.Metadata) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -665,6 +713,40 @@ func (m *MsgTransfer) Unmarshal(dAtA []byte) error { break } } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Metadata = append(m.Metadata[:0], dAtA[iNdEx:postIndex]...) + if m.Metadata == nil { + m.Metadata = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/proto/ibc/applications/interchain_accounts/controller/v1/tx.proto b/proto/ibc/applications/interchain_accounts/controller/v1/tx.proto new file mode 100644 index 00000000000..c4cd4ba31b5 --- /dev/null +++ b/proto/ibc/applications/interchain_accounts/controller/v1/tx.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; + +package ibc.applications.interchain_accounts.controller.v1; + +option go_package = "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/controller/types"; + +import "gogoproto/gogo.proto"; +import "ibc/applications/interchain_accounts/v1/packet.proto"; + +// Msg defines the 27-interchain-accounts/controller Msg service. +service Msg { + // RegisterInterchainAccount defines a rpc handler for MsgRegisterInterchainAccount. + rpc RegisterInterchainAccount(MsgRegisterInterchainAccount) returns (MsgRegisterInterchainAccountResponse); + // SendTx defines a rpc handler for MsgSendTx. + rpc SendTx(MsgSendTx) returns (MsgSendTxResponse); +} + +// MsgRegisterInterchainAccount defines the payload for Msg/RegisterAccount +message MsgRegisterInterchainAccount { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string owner = 1; + string connection_id = 2 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + string version = 3; +} + +// MsgRegisterInterchainAccountResponse defines the response for Msg/RegisterAccount +message MsgRegisterInterchainAccountResponse { + string channel_id = 1 [(gogoproto.moretags) = "yaml:\"channel_id\""]; +} + +// MsgSendTx defines the payload for Msg/SendTx +message MsgSendTx { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string owner = 1; + string connection_id = 2 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + ibc.applications.interchain_accounts.v1.InterchainAccountPacketData packet_data = 3 + [(gogoproto.moretags) = "yaml:\"packet_data\"", (gogoproto.nullable) = false]; + // Relative timeout timestamp provided will be added to the current block time during transaction execution. + // The timeout timestamp must be non-zero. + uint64 relative_timeout = 4 [(gogoproto.moretags) = "yaml:\"relative_timeout\""]; +} + +// MsgSendTxResponse defines the response for MsgSendTx +message MsgSendTxResponse { + uint64 sequence = 1; +} diff --git a/proto/ibc/applications/transfer/v1/tx.proto b/proto/ibc/applications/transfer/v1/tx.proto index b8fe0d5feda..4d663823531 100644 --- a/proto/ibc/applications/transfer/v1/tx.proto +++ b/proto/ibc/applications/transfer/v1/tx.proto @@ -38,6 +38,8 @@ message MsgTransfer { // Timeout timestamp in absolute nanoseconds since unix epoch. // The timeout is disabled when set to 0. uint64 timeout_timestamp = 7 [(gogoproto.moretags) = "yaml:\"timeout_timestamp\""]; + // optional metadata + bytes metadata = 8; } // MsgTransferResponse defines the Msg/Transfer response type. diff --git a/proto/ibc/applications/transfer/v2/packet.proto b/proto/ibc/applications/transfer/v2/packet.proto index 850320df340..e42d11f7ccd 100644 --- a/proto/ibc/applications/transfer/v2/packet.proto +++ b/proto/ibc/applications/transfer/v2/packet.proto @@ -16,4 +16,6 @@ message FungibleTokenPacketData { string sender = 3; // the recipient address on the destination chain string receiver = 4; + // optional metadata + bytes metadata = 5; }