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

adr 8 callback packet data impl followups #3325

Merged
merged 36 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1bdb0e9
remove query client (#3227)
expertdicer Mar 14, 2023
be4c2d0
Rename ``IsBound`` to ``HasCapability`` (#3253)
expertdicer Mar 14, 2023
036565a
chore: add support for tendermint debug log level (#3279)
colin-axner Mar 14, 2023
80bab81
build(deps): bump cosmossdk.io/math from 1.0.0-beta.6.0.2023021617212…
dependabot[bot] Mar 14, 2023
2cb28d7
Write docker inspect output to diagnostics (#3291)
chatton Mar 14, 2023
02d8975
chore: fix dead links (#3293)
crodriguezvega Mar 14, 2023
796c7bc
build(deps): bump google.golang.org/protobuf from 1.29.0 to 1.29.1 (#…
dependabot[bot] Mar 15, 2023
71e8bf4
deps: bump SDK v0.47 (#3295)
crodriguezvega Mar 15, 2023
94d74f9
remove unnecessary import from doc
crodriguezvega Mar 15, 2023
b502f47
chore: remove support for v3 (#3294)
crodriguezvega Mar 17, 2023
6be595a
build(deps): bump actions/setup-go from 3 to 4 (#3307)
dependabot[bot] Mar 17, 2023
85faf55
imp: remove unnecessary defer func statements (#3304)
GNaD13 Mar 20, 2023
b4a386a
Remove gogoproto yaml tags from proto files (#3290)
hieuvubk Mar 20, 2023
b3f1b4d
add reasoning for migration to enable localhost
crodriguezvega Mar 20, 2023
cbb6859
Support configuration file for e2e tests (#3260)
chatton Mar 21, 2023
89ecf25
E2E fzf Test Selection Autocompletion (#3313)
chatton Mar 21, 2023
88f3c3c
post v7 release chores (#3310)
crodriguezvega Mar 21, 2023
5a67efc
chore: fix linter warnings (#3311)
crodriguezvega Mar 22, 2023
7d23e0f
ADR 008: IBC Actor Callbacks (#1976)
AdityaSripal Mar 22, 2023
8c16ce0
lint: fix spelling
colin-axner Mar 22, 2023
2818a57
chore: apply self review concerns
colin-axner Mar 22, 2023
8e8c64a
chore: rename CallbackPacketDataI to CallbackPacketData
colin-axner Mar 22, 2023
3cdc8b3
chore: finish applying remaining review suggestions
colin-axner Mar 22, 2023
be5a766
test: add remaining unit tests for transfer and ica
colin-axner Mar 22, 2023
767c5af
test: add unmarshaling test
colin-axner Mar 22, 2023
12c00ec
imp: address ADR 8 review suggestions (#3319)
colin-axner Mar 22, 2023
4961826
Bump interchain test (#3314)
chatton Mar 23, 2023
a02ec51
fix: remove codec registration
colin-axner Mar 23, 2023
6f25b8e
fix: build + linting
colin-axner Mar 23, 2023
f4c20e4
Only run e2e on R4R (#3330)
chatton Mar 23, 2023
8971ffc
fix fork workflows (#3328)
chatton Mar 23, 2023
1c6164b
Merge branch 'main' of github.com:cosmos/ibc-go into colin/callback-p…
colin-axner Mar 23, 2023
9435525
Revert "Merge branch 'main' of github.com:cosmos/ibc-go into colin/ca…
colin-axner Mar 23, 2023
7f88419
chore: add optional interface godoc
colin-axner Mar 23, 2023
935c5f0
Apply suggestions from code review
colin-axner Mar 27, 2023
a1252f3
chore: use backticks instead of escape characters in testing
colin-axner Mar 27, 2023
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
6 changes: 0 additions & 6 deletions modules/apps/27-interchain-accounts/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/gogoproto/proto"
"github.com/cosmos/ibc-go/v7/modules/core/exported"
)

// ModuleCdc references the global interchain accounts module codec. Note, the codec
Expand All @@ -22,11 +21,6 @@ var ModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
registry.RegisterImplementations((*authtypes.AccountI)(nil), &InterchainAccount{})
registry.RegisterImplementations((*authtypes.GenesisAccount)(nil), &InterchainAccount{})

registry.RegisterImplementations(
(*exported.CallbackPacketDataI)(nil),
&InterchainAccountPacketData{},
)
chatton marked this conversation as resolved.
Show resolved Hide resolved
}

// SerializeCosmosTx serializes a slice of sdk.Msg's using the CosmosTx type. The sdk.Msg's are
Expand Down
37 changes: 22 additions & 15 deletions modules/apps/27-interchain-accounts/types/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var (
DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds())
)

var _ exported.CallbackPacketDataI = (*InterchainAccountPacketData)(nil)
var _ exported.CallbackPacketData = (*InterchainAccountPacketData)(nil)
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 this change 👍


// ValidateBasic performs basic validation of the interchain account packet data.
// The memo may be empty.
Expand Down Expand Up @@ -55,7 +55,7 @@ func (iapd InterchainAccountPacketData) GetBytes() []byte {

ADR-8 CallbackPacketData implementation

InterchainAccountPacketData implements CallbackPacketDataI interface. This will allow middlewares targetting specific VMs
InterchainAccountPacketData implements CallbackPacketDataI interface. This will allow middlewares targeting specific VMs
to retrieve the desired callback addresses for the ICA packet on the source and destination chains.

The Memo is used to set the desired callback addresses.
Expand All @@ -66,27 +66,32 @@ The Memo format is defined like so:
{
// ... other memo fields we don't care about
"callbacks": {
"src_callback_address": {contractAddrOnSrcChain},
"src_callback_address": {contractAddrOnSourceChain},
"dest_callback_address": {contractAddrOnDestChain},
crodriguezvega marked this conversation as resolved.
Show resolved Hide resolved
"src_callback_msg": {jsonObjectForSrcChainCallback}, // optional field
"dest_callback_msg": {jsonObjectForDestChainCallback}, // optional field
}

// optional fields
"src_callback_msg": {jsonObjectForSourceChainCallback},
"dest_callback_msg": {jsonObjectForDestChainCallback},
}
}
```

*/

// GetSrcCallbackAddress returns the callback address on the source chain.
// ADR-8 middleware should callback on the sender address on the source chain
// if the sender address is an IBC Actor (i.e. smart contract that accepts IBC callbacks)
func (iapd InterchainAccountPacketData) GetSrcCallbackAddress() string {
// GetSourceCallbackAddress returns the source callback address provided in the packet data memo.
// If no callback address is specified, an empty string is returned.
//
// The memo is expected to specify the callback address in the following format:
// { "callbacks": { "src_callback_address": {contractAddrOnSourceChain}}
//
// ADR-8 middleware should callback on the returned address if it is a PacketActor
// (i.e. smart contract that accepts IBC callbacks).
func (iapd InterchainAccountPacketData) GetSourceCallbackAddress() string {
crodriguezvega marked this conversation as resolved.
Show resolved Hide resolved
if len(iapd.Memo) == 0 {
return ""
}

jsonObject := make(map[string]interface{})
// the jsonObject must be a valid JSON object
err := json.Unmarshal([]byte(iapd.Memo), &jsonObject)
if err != nil {
return ""
Expand All @@ -101,17 +106,19 @@ func (iapd InterchainAccountPacketData) GetSrcCallbackAddress() string {
if !ok {
return ""
}

return callbackAddr
}

// GetDestCallbackAddress returns the callback address on the destination chain.
// ADR-8 middleware should callback on the receiver address on the destination chain
// if the receiver address is an IBC Actor (i.e. smart contract that accepts IBC callbacks)
// GetDestCallbackAddress returns an empty string. Destination callback addresses
// are not supported for ICS 27 since this feature is natively supported by
// interchain accounts host submodule transaction execution.
func (iapd InterchainAccountPacketData) GetDestCallbackAddress() string {
return ""
}

// UserDefinedGasLimit no-ops on this method to use relayer passed in gas
// UserDefinedGasLimit returns 0 (no-op). The gas limit of the executing
// transaction will be used.
func (fptd InterchainAccountPacketData) UserDefinedGasLimit() uint64 {
return 0
}
Expand Down
82 changes: 68 additions & 14 deletions modules/apps/27-interchain-accounts/types/packet_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types_test

import (
"fmt"

"github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types"
)

Expand Down Expand Up @@ -83,11 +85,13 @@ func (suite *TypesTestSuite) TestValidateBasic() {
}
}

func (suite *TypesTestSuite) TestGetCallbackAddresses() {
func (suite *TypesTestSuite) TestGetSourceCallbackAddress() {
expSrcCbAddr := "srcCbAddr"
colin-axner marked this conversation as resolved.
Show resolved Hide resolved

testCases := []struct {
name string
packetData types.InterchainAccountPacketData
expSrcCallbackAddr string
name string
packetData types.InterchainAccountPacketData
expPass bool
}{
{
"memo is empty",
Expand All @@ -96,7 +100,7 @@ func (suite *TypesTestSuite) TestGetCallbackAddresses() {
Data: []byte("data"),
Memo: "",
},
"",
false,
},
{
"memo is not json string",
Expand All @@ -105,7 +109,7 @@ func (suite *TypesTestSuite) TestGetCallbackAddresses() {
Data: []byte("data"),
Memo: "memo",
},
"",
false,
},
{
"memo is does not have callbacks in json struct",
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -114,7 +118,7 @@ func (suite *TypesTestSuite) TestGetCallbackAddresses() {
Data: []byte("data"),
Memo: "{\"Key\": 10}",
},
"",
false,
},
{
"memo has callbacks in json struct but does not have src_callback_address key",
Expand All @@ -123,7 +127,7 @@ func (suite *TypesTestSuite) TestGetCallbackAddresses() {
Data: []byte("data"),
Memo: "{\"callbacks\": {\"Key\": 10}}",
},
"",
false,
},
{
"memo has callbacks in json struct but does not have string value for src_callback_address key",
Expand All @@ -132,21 +136,71 @@ func (suite *TypesTestSuite) TestGetCallbackAddresses() {
Data: []byte("data"),
Memo: "{\"callbacks\": {\"src_callback_address\": 10}}",
},
"",
false,
},
{
"memo has callbacks in json struct but does not have string value for src_callback_address key",
"memo has callbacks in json struct and properly formatted src_callback_address",
types.InterchainAccountPacketData{
Type: types.EXECUTE_TX,
Data: []byte("data"),
Memo: fmt.Sprintf("{\"callbacks\": {\"src_callback_address\": \"%s\"}}", expSrcCbAddr),
},
true,
},
}

for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
srcCbAddr := tc.packetData.GetSourceCallbackAddress()

if tc.expPass {
suite.Require().Equal(expSrcCbAddr, srcCbAddr)
} else {
suite.Require().Equal("", srcCbAddr)
}
})
}
}

func (suite *TypesTestSuite) TestGetDestCallbackAddress() {
Copy link
Member

Choose a reason for hiding this comment

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

Seems like an odd test to have for a no-op function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If unresolved merge conflicts cause this to return a non empty string, I'd prefer the tests to catch the issue. It's quite easy for merge conflicts to slip through

testCases := []struct {
name string
packetData types.InterchainAccountPacketData
}{
{
"memo is empty",
types.InterchainAccountPacketData{
Type: types.EXECUTE_TX,
Data: []byte("data"),
Memo: "{\"callbacks\": {\"src_callback_address\": \"testAddress\"}}",
Memo: "",
},
},
{
"memo has dest callback address specified in json struct",
types.InterchainAccountPacketData{
Type: types.EXECUTE_TX,
Data: []byte("data"),
Memo: "{\"callbacks\": {\"dest_callback_address\": \"testAddress\"}}",
},
"testAddress",
},
}

for _, tc := range testCases {
srcCbAddr := tc.packetData.GetSrcCallbackAddress()
suite.Require().Equal(tc.expSrcCallbackAddr, srcCbAddr, "%s testcase does not have expected src_callback_address", tc.name)
tc := tc
suite.Run(tc.name, func() {
destCbAddr := tc.packetData.GetDestCallbackAddress()
suite.Require().Equal("", destCbAddr)
})
}
}

func (suite *TypesTestSuite) TestUserDefinedGasLimit() {
packetData := types.InterchainAccountPacketData{
Type: types.EXECUTE_TX,
Data: []byte("data"),
Memo: "{\"callbacks\": {\"user_defined_gas_limit\": 100}}",
}

suite.Require().Equal(uint64(0), packetData.UserDefinedGasLimit(), "user defined gas limit does not return 0")
}
13 changes: 3 additions & 10 deletions modules/apps/transfer/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ package types
import (
"bytes"

"github.com/cosmos/cosmos-sdk/x/authz"
"github.com/cosmos/gogoproto/jsonpb"
"github.com/cosmos/gogoproto/proto"
"github.com/cosmos/ibc-go/v7/modules/core/exported"

"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/msgservice"
"github.com/cosmos/cosmos-sdk/x/authz"
"github.com/cosmos/gogoproto/jsonpb"
"github.com/cosmos/gogoproto/proto"
)

// RegisterLegacyAminoCodec registers the necessary x/ibc transfer interfaces and concrete types
Expand All @@ -30,11 +28,6 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
&TransferAuthorization{},
)

registry.RegisterImplementations(
(*exported.CallbackPacketDataI)(nil),
&FungibleTokenPacketData{},
)

msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
}

Expand Down
58 changes: 36 additions & 22 deletions modules/apps/transfer/types/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var (
DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds())
)

var _ exported.CallbackPacketDataI = (*FungibleTokenPacketData)(nil)
var _ exported.CallbackPacketData = (*FungibleTokenPacketData)(nil)

// NewFungibleTokenPacketData contructs a new FungibleTokenPacketData instance
func NewFungibleTokenPacketData(
Expand Down Expand Up @@ -67,11 +67,11 @@ func (ftpd FungibleTokenPacketData) GetBytes() []byte {
return sdk.MustSortJSON(mustProtoMarshalJSON(&ftpd))
}

/**
/*

ADR-8 CallbackPacketData implementation

FungibleTokenPacketData implements CallbackPacketDataI interface. This will allow middlewares targetting specific VMs
FungibleTokenPacketData implements CallbackPacketDataI interface. This will allow middlewares targeting specific VMs
to retrieve the desired callback addresses for the ICS20 packet on the source and destination chains.

The Memo is used to ensure that the callback is desired by the user. This allows a user to send an ICS20 packet
Expand All @@ -84,32 +84,38 @@ The Memo format is defined like so:
{
// ... other memo fields we don't care about
"callbacks": {
"src_callback_address": {contractAddrOnSrcChain},
"src_callback_address": {contractAddrOnSourceChain},
"dest_callback_address": {contractAddrOnDestChain},
"src_callback_msg": {jsonObjectForSrcChainCallback},

// optional fields
"src_callback_msg": {jsonObjectForSourceChainCallback},
"dest_callback_msg": {jsonObjectForDestChainCallback},
}

}
```

For transfer, we will enforce that the src_callback_address is the same as sender and dest_callback_address is the same as receiver.
However, we may remove this restriction at a later date if it proves useful.

**/

// ADR-8 middleware should callback on the sender address on the source chain
// if the sender address is an IBC Actor (i.e. smart contract that accepts IBC callbacks)
// The desired callback address must be confirmed in the memo under the "callbacks" key.
// This ensures that the callback is explicitly desired by the user and not just called
// automatically.
func (ftpd FungibleTokenPacketData) GetSrcCallbackAddress() string {
*/

// GetSourceCallbackAddress returns the sender address if it is also specified in
// the packet data memo. The desired callback address must be confirmed in the
// memo under the "callbacks" key. This ensures that the callback is explicitly
// desired by the user and not called automatically. If no callback address is
// specified, an empty string is returned.
//
// The memo is expected to contain the source callback address in the following format:
// { "callbacks": { "src_callback_address": {contractAddrOnSourceChain}}
//
// ADR-8 middleware should callback on the returned address if it is a PacketActor
// (i.e. smart contract that accepts IBC callbacks).
func (ftpd FungibleTokenPacketData) GetSourceCallbackAddress() string {
if len(ftpd.Memo) == 0 {
return ""
}

jsonObject := make(map[string]interface{})
// the jsonObject must be a valid JSON object
err := json.Unmarshal([]byte(ftpd.Memo), &jsonObject)
if err != nil {
return ""
Expand All @@ -123,21 +129,27 @@ func (ftpd FungibleTokenPacketData) GetSrcCallbackAddress() string {
if callbackData["src_callback_address"] == ftpd.Sender {
return ftpd.Sender
}

return ""
}

// ADR-8 middleware should callback on the receiver address on the destination chain
// if the receiver address is an IBC Actor (i.e. smart contract that accepts IBC callbacks)
// The desired callback address must be confirmed in the memo under the "callbacks" key.
// This ensures that the callback is explicitly desired by the user and not just called
// automatically.
// GetDestCallbackAddress returns the receiving address if it is also specified in
// the packet data memo. The desired callback address must be confirmed in the
// memo under the "callbacks" key. This ensures that the callback is explicitly
// desired by the user and not called automatically. If no callback address is
// specified, an empty string is returned.
//
// The memo is expected to contain the destination callback address in the following format:
// { "callbacks": { "dest_callback_address": {contractAddrOnDestChain}}
//
// ADR-8 middleware should callback on the returned address if it is a PacketActor
// (i.e. smart contract that accepts IBC callbacks).
func (ftpd FungibleTokenPacketData) GetDestCallbackAddress() string {
if len(ftpd.Memo) == 0 {
return ""
}

jsonObject := make(map[string]interface{})
// the jsonObject must be a valid JSON object
err := json.Unmarshal([]byte(ftpd.Memo), &jsonObject)
if err != nil {
return ""
Expand All @@ -151,10 +163,12 @@ func (ftpd FungibleTokenPacketData) GetDestCallbackAddress() string {
if callbackData["dest_callback_address"] == ftpd.Receiver {
return ftpd.Receiver
}

return ""
}

// no-op on this method to use relayer passed in gas
// UserDefinedGasLimit returns 0 (no-op). The gas limit of the executing
// transaction will be used.
func (fptd FungibleTokenPacketData) UserDefinedGasLimit() uint64 {
return 0
}
Loading