Skip to content

Commit

Permalink
feat(core, apps): 'PacketData' interface added and implemented (#4200)
Browse files Browse the repository at this point in the history
* refactor(core/exported): moved packet interfaces to packet.go

* feat(core/exported): created PacketData interface

* feat(transfer): implemented PacketData for transfer

* feat(ica): implemented PacketData

* docs(transfer.adr8): updated godocs of GetPacketSender

* style(adr8): renamed parameter srcPortID -> sourcePortID

* docs(core.adr8): updated godocs for PacketData interface

* docs(core.adr8): improved godocs for PacketData interface

* docs(core.adr8): updated godocs

* docs(transfer.adr8): updated godocs

* style(ica_test): removed unneeded comment

* docs(ica.adr8): updated godocs

* style(ica): fixed revive linter complaint

* docs(transfer.adr8): updated GetPacketSender's godocs

* style(transfer, ica): ran golangci-lin

(cherry picked from commit ec68438)

# Conflicts:
#	modules/apps/27-interchain-accounts/types/packet.go
#	modules/apps/27-interchain-accounts/types/packet_test.go
#	modules/apps/transfer/types/packet.go
#	modules/apps/transfer/types/packet_test.go
#	modules/core/exported/packet.go
  • Loading branch information
srdtrk authored and mergify[bot] committed Aug 1, 2023
1 parent 8802471 commit c30b538
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 0 deletions.
54 changes: 54 additions & 0 deletions modules/apps/27-interchain-accounts/types/packet.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
package types

import (
<<<<<<< HEAD
=======
"encoding/json"
"strings"
>>>>>>> ec684384 (feat(core, apps): 'PacketData' interface added and implemented (#4200))
"time"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

<<<<<<< HEAD
=======
var (
_ ibcexported.PacketData = (*InterchainAccountPacketData)(nil)
_ ibcexported.PacketDataProvider = (*InterchainAccountPacketData)(nil)
)

>>>>>>> ec684384 (feat(core, apps): 'PacketData' interface added and implemented (#4200))
// MaxMemoCharLength defines the maximum length for the InterchainAccountPacketData memo field
const MaxMemoCharLength = 256

Expand Down Expand Up @@ -63,3 +76,44 @@ func (ct CosmosTx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {

return nil
}
<<<<<<< HEAD
=======

// GetPacketSender returns the sender address of the interchain accounts packet data.
// It is obtained from the source port ID by cutting off the ControllerPortPrefix.
// If the source port ID does not have the ControllerPortPrefix, then an empty string is returned.
//
// NOTE:
// - The sender address is set by the packet sender and may not have been validated a signature
// check if the packet sender isn't the interchain accounts module.
// - The sender address must only be used by modules on the sending chain.
func (InterchainAccountPacketData) GetPacketSender(sourcePortID string) string {
icaOwner, found := strings.CutPrefix(sourcePortID, ControllerPortPrefix)
if !found {
return ""
}
return icaOwner
}

// GetCustomPacketData interprets the memo field of the packet data as a JSON object
// and returns the value associated with the given key.
// If the key is missing or the memo is not properly formatted, then nil is returned.
func (iapd InterchainAccountPacketData) GetCustomPacketData(key string) interface{} {
if len(iapd.Memo) == 0 {
return nil
}

jsonObject := make(map[string]interface{})
err := json.Unmarshal([]byte(iapd.Memo), &jsonObject)
if err != nil {
return nil
}

memoData, found := jsonObject[key]
if !found {
return nil
}

return memoData
}
>>>>>>> ec684384 (feat(core, apps): 'PacketData' interface added and implemented (#4200))
98 changes: 98 additions & 0 deletions modules/apps/27-interchain-accounts/types/packet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,101 @@ func (suite *TypesTestSuite) TestValidateBasic() {
})
}
}
<<<<<<< HEAD
=======

func (suite *TypesTestSuite) TestGetPacketSender() {
testCases := []struct {
name string
srcPortID string
expSender string
}{
{
"success: port id has prefix",
types.ControllerPortPrefix + ibctesting.TestAccAddress,
ibctesting.TestAccAddress,
},
{
"failure: missing prefix",
ibctesting.TestAccAddress,
"",
},
{
"failure: empty port id",
"",
"",
},
}

for _, tc := range testCases {
packetData := types.InterchainAccountPacketData{}
suite.Require().Equal(tc.expSender, packetData.GetPacketSender(tc.srcPortID))
}
}

func (suite *TypesTestSuite) TestPacketDataProvider() {
expCallbackAddr := ibctesting.TestAccAddress

testCases := []struct {
name string
packetData types.InterchainAccountPacketData
expCustomData interface{}
}{
{
"success: src_callback key in memo",
types.InterchainAccountPacketData{
Type: types.EXECUTE_TX,
Data: []byte("data"),
Memo: fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, expCallbackAddr),
},
map[string]interface{}{
"address": expCallbackAddr,
},
},
{
"success: src_callback key in memo with additional fields",
types.InterchainAccountPacketData{
Type: types.EXECUTE_TX,
Data: []byte("data"),
Memo: fmt.Sprintf(`{"src_callback": {"address": "%s", "gas_limit": "200000"}}`, expCallbackAddr),
},
map[string]interface{}{
"address": expCallbackAddr,
"gas_limit": "200000",
},
},
{
"success: src_callback has string valu",
types.InterchainAccountPacketData{
Type: types.EXECUTE_TX,
Data: []byte("data"),
Memo: `{"src_callback": "string"}`,
},
"string",
},
{
"failure: empty memo",
types.InterchainAccountPacketData{
Type: types.EXECUTE_TX,
Data: []byte("data"),
Memo: "",
},
nil,
},
{
"failure: non-json memo",
types.InterchainAccountPacketData{
Type: types.EXECUTE_TX,
Data: []byte("data"),
Memo: "invalid",
},
nil,
},
}

for _, tc := range testCases {
customData := tc.packetData.GetCustomPacketData("src_callback")
suite.Require().Equal(tc.expCustomData, customData)
}
}
>>>>>>> ec684384 (feat(core, apps): 'PacketData' interface added and implemented (#4200))
44 changes: 44 additions & 0 deletions modules/apps/transfer/types/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

<<<<<<< HEAD
=======
var (
_ ibcexported.PacketData = (*FungibleTokenPacketData)(nil)
_ ibcexported.PacketDataProvider = (*FungibleTokenPacketData)(nil)
)

>>>>>>> ec684384 (feat(core, apps): 'PacketData' interface added and implemented (#4200))
var (
// DefaultRelativePacketTimeoutHeight is the default packet timeout height (in blocks) relative
// to the current block height of the counterparty chain provided by the client state. The
Expand Down Expand Up @@ -60,3 +68,39 @@ func (ftpd FungibleTokenPacketData) ValidateBasic() error {
func (ftpd FungibleTokenPacketData) GetBytes() []byte {
return sdk.MustSortJSON(mustProtoMarshalJSON(&ftpd))
}
<<<<<<< HEAD
=======

// GetPacketSender returns the sender address embedded in the packet data.
//
// NOTE:
// - The sender address is set by the module which requested the packet to be sent,
// and this module may not have validated the sender address by a signature check.
// - The sender address must only be used by modules on the sending chain.
// - sourcePortID is not used in this implementation.
func (ftpd FungibleTokenPacketData) GetPacketSender(sourcePortID string) string {
return ftpd.Sender
}

// GetCustomPacketData interprets the memo field of the packet data as a JSON object
// and returns the value associated with the given key.
// If the key is missing or the memo is not properly formatted, then nil is returned.
func (ftpd FungibleTokenPacketData) GetCustomPacketData(key string) interface{} {
if len(ftpd.Memo) == 0 {
return nil
}

jsonObject := make(map[string]interface{})
err := json.Unmarshal([]byte(ftpd.Memo), &jsonObject)
if err != nil {
return nil
}

memoData, found := jsonObject[key]
if !found {
return nil
}

return memoData
}
>>>>>>> ec684384 (feat(core, apps): 'PacketData' interface added and implemented (#4200))
89 changes: 89 additions & 0 deletions modules/apps/transfer/types/packet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,92 @@ func TestFungibleTokenPacketDataValidateBasic(t *testing.T) {
}
}
}
<<<<<<< HEAD
=======

func (suite *TypesTestSuite) TestGetPacketSender() {
packetData := types.FungibleTokenPacketData{
Denom: denom,
Amount: amount,
Sender: sender,
Receiver: receiver,
Memo: "",
}

suite.Require().Equal(sender, packetData.GetPacketSender(types.PortID))
}

func (suite *TypesTestSuite) TestPacketDataProvider() {
testCases := []struct {
name string
packetData types.FungibleTokenPacketData
expCustomData interface{}
}{
{
"success: src_callback key in memo",
types.FungibleTokenPacketData{
Denom: denom,
Amount: amount,
Sender: sender,
Receiver: receiver,
Memo: fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, receiver),
},
map[string]interface{}{
"address": receiver,
},
},
{
"success: src_callback key in memo with additional fields",
types.FungibleTokenPacketData{
Denom: denom,
Amount: amount,
Sender: sender,
Receiver: receiver,
Memo: fmt.Sprintf(`{"src_callback": {"address": "%s", "gas_limit": "200000"}}`, receiver),
},
map[string]interface{}{
"address": receiver,
"gas_limit": "200000",
},
},
{
"success: src_callback has string value",
types.FungibleTokenPacketData{
Denom: denom,
Amount: amount,
Sender: sender,
Receiver: receiver,
Memo: `{"src_callback": "string"}`,
},
"string",
},
{
"failure: empty memo",
types.FungibleTokenPacketData{
Denom: denom,
Amount: amount,
Sender: sender,
Receiver: receiver,
Memo: "",
},
nil,
},
{
"failure: non-json memo",
types.FungibleTokenPacketData{
Denom: denom,
Amount: amount,
Sender: sender,
Receiver: receiver,
Memo: "invalid",
},
nil,
},
}

for _, tc := range testCases {
customData := tc.packetData.GetCustomPacketData("src_callback")
suite.Require().Equal(tc.expCustomData, customData)
}
}
>>>>>>> ec684384 (feat(core, apps): 'PacketData' interface added and implemented (#4200))
52 changes: 52 additions & 0 deletions modules/core/exported/packet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package exported

// PacketI defines the standard interface for IBC packets
type PacketI interface {
GetSequence() uint64
GetTimeoutHeight() Height
GetTimeoutTimestamp() uint64
GetSourcePort() string
GetSourceChannel() string
GetDestPort() string
GetDestChannel() string
GetData() []byte
ValidateBasic() error
}

// Acknowledgement defines the interface used to return acknowledgements in the OnRecvPacket callback.
// The Acknowledgement interface is used by core IBC to ensure partial state changes are not committed
// when packet receives have not properly succeeded (typically resulting in an error acknowledgement being returned).
// The interface also allows core IBC to obtain the acknowledgement bytes whose encoding is determined by each IBC application or middleware.
// Each custom acknowledgement type must implement this interface.
type Acknowledgement interface {
// Success determines if the IBC application state should be persisted when handling `RecvPacket`.
// During `OnRecvPacket` IBC application callback execution, all state changes are held in a cache store and committed if:
// - the acknowledgement.Success() returns true
// - a nil acknowledgement is returned (asynchronous acknowledgements)
//
// Note 1: IBC application callback events are always persisted so long as `RecvPacket` succeeds without error.
//
// Note 2: The return value should account for the success of the underlying IBC application or middleware. Thus the `acknowledgement.Success` is representative of the entire IBC stack's success when receiving a packet. The individual success of each acknowledgement associated with an IBC application or middleware must be determined by obtaining the actual acknowledgement type after decoding the acknowledgement bytes.
//
// See https://github.com/cosmos/ibc-go/blob/v7.0.0/docs/ibc/apps.md for further explanations.
Success() bool
Acknowledgement() []byte
}

// PacketData defines an optional interface which an application's packet data structure may implement.
type PacketData interface {
// GetPacketSender returns the sender address of the packet data.
// If the packet sender is unknown or undefined, an empty string should be returned.
GetPacketSender(sourcePortID string) string
}

// PacketDataProvider defines an optional interfaces for retrieving custom packet data stored on behalf of another application.
// An existing problem in the IBC middleware design is the inability for a middleware to define its own packet data type and insert packet sender provided information.
// A short term solution was introduced into several application's packet data to utilize a memo field to carry this information on behalf of another application.
// This interfaces standardizes that behaviour. Upon realization of the ability for middleware's to define their own packet data types, this interface will be deprecated and removed with time.
type PacketDataProvider interface {
// GetCustomPacketData returns the packet data held on behalf of another application.
// The name the information is stored under should be provided as the key.
// If no custom packet data exists for the key, nil should be returned.
GetCustomPacketData(key string) interface{}
}

0 comments on commit c30b538

Please sign in to comment.