Skip to content

Commit

Permalink
add ibc hook testings
Browse files Browse the repository at this point in the history
  • Loading branch information
beer-1 committed Mar 27, 2024
1 parent b896c11 commit 41af3f1
Show file tree
Hide file tree
Showing 21 changed files with 559 additions and 117 deletions.
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ func NewMinitiaApp(
packetForwardMiddleware,
ibchooks.NewICS4Middleware(
nil, /* ics4wrapper: not used */
ibcevmhooks.NewEVMHooks(app.EVMKeeper, ac),
ibcevmhooks.NewEVMHooks(appCodec, ac, app.EVMKeeper),
),
app.IBCHooksKeeper,
)
Expand Down
3 changes: 2 additions & 1 deletion app/ibc-hooks/ack.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ func (h EVMHooks) onAckIcs20Packet(
if allowed, err := h.checkACL(im, ctx, callback.ContractAddress); err != nil {
return err
} else if !allowed {
// just return nil here to avoid packet stuck due to hook acl.
return nil
}

inputBz, err := h.asyncCallbackABI.Pack(functionNameAck, callback.Id, !isAckError(acknowledgement))
inputBz, err := h.asyncCallbackABI.Pack(functionNameAck, callback.Id, !isAckError(h.codec, acknowledgement))
if err != nil {
return err
}
Expand Down
119 changes: 119 additions & 0 deletions app/ibc-hooks/ack_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package evm_hooks_test

import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"strings"
"testing"

transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
"github.com/initia-labs/minievm/x/evm/contracts/counter"
"github.com/stretchr/testify/require"
)

func Test_onAckIcs20Packet_noMemo(t *testing.T) {
ctx, input := createDefaultTestInput(t)
_, _, addr := keyPubAddr()
_, _, addr2 := keyPubAddr()

data := transfertypes.FungibleTokenPacketData{
Denom: "foo",
Amount: "10000",
Sender: addr.String(),
Receiver: addr2.String(),
Memo: "",
}

dataBz, err := json.Marshal(&data)
require.NoError(t, err)

ackBz, err := json.Marshal(channeltypes.NewResultAcknowledgement([]byte{byte(1)}))
require.NoError(t, err)

err = input.IBCHooksMiddleware.OnAcknowledgementPacket(ctx, channeltypes.Packet{
Data: dataBz,
}, ackBz, addr)
require.NoError(t, err)
}

func Test_onAckIcs20Packet_memo(t *testing.T) {
ctx, input := createDefaultTestInput(t)
_, _, addr := keyPubAddr()
evmAddr := common.BytesToAddress(addr.Bytes())

codeBz, err := hex.DecodeString(strings.TrimPrefix(counter.CounterBin, "0x"))
require.NoError(t, err)

_, contractAddr, err := input.EVMKeeper.EVMCreate(ctx, evmAddr, codeBz)
require.NoError(t, err)

abi, err := counter.CounterMetaData.GetAbi()
require.NoError(t, err)

data := transfertypes.FungibleTokenPacketData{
Denom: "foo",
Amount: "10000",
Sender: addr.String(),
Receiver: contractAddr.Hex(),
Memo: fmt.Sprintf(`{
"evm": {
"async_callback": {
"id": 99,
"contract_address": "%s"
}
}
}`, contractAddr.Hex()),
}

dataBz, err := json.Marshal(&data)
require.NoError(t, err)

successAckBz := channeltypes.NewResultAcknowledgement([]byte{byte(1)}).Acknowledgement()
failedAckBz := channeltypes.NewErrorAcknowledgement(errors.New("failed")).Acknowledgement()

// hook should not be called to due to acl
err = input.IBCHooksMiddleware.OnAcknowledgementPacket(ctx, channeltypes.Packet{
Data: dataBz,
}, successAckBz, addr)
require.NoError(t, err)

// check the contract state
queryInputBz, err := abi.Pack("count")
require.NoError(t, err)
queryRes, logs, err := input.EVMKeeper.EVMCall(ctx, evmAddr, contractAddr, queryInputBz)
require.NoError(t, err)
require.Equal(t, uint256.NewInt(0).Bytes32(), [32]byte(queryRes))
require.Empty(t, logs)

// set acl
require.NoError(t, input.IBCHooksKeeper.SetAllowed(ctx, contractAddr[:], true))

// success with success ack
err = input.IBCHooksMiddleware.OnAcknowledgementPacket(ctx, channeltypes.Packet{
Data: dataBz,
}, successAckBz, addr)
require.NoError(t, err)

// check the contract state; increased by 99 if ack is success
queryRes, logs, err = input.EVMKeeper.EVMCall(ctx, evmAddr, contractAddr, queryInputBz)
require.NoError(t, err)
require.Equal(t, uint256.NewInt(99).Bytes32(), [32]byte(queryRes))
require.Empty(t, logs)

// success with failed ack
err = input.IBCHooksMiddleware.OnAcknowledgementPacket(ctx, channeltypes.Packet{
Data: dataBz,
}, failedAckBz, addr)
require.NoError(t, err)

// check the contract state; increased by 1 if ack is failed
queryRes, logs, err = input.EVMKeeper.EVMCall(ctx, evmAddr, contractAddr, queryInputBz)
require.NoError(t, err)
require.Equal(t, uint256.NewInt(100).Bytes32(), [32]byte(queryRes))
require.Empty(t, logs)
}
9 changes: 6 additions & 3 deletions app/ibc-hooks/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"github.com/cosmos/gogoproto/proto"

capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types"
ibc "github.com/cosmos/ibc-go/v8/modules/core"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types"
Expand All @@ -52,7 +53,7 @@ import (

evmhooks "github.com/initia-labs/minievm/app/ibc-hooks"
"github.com/initia-labs/minievm/x/evm"
EVMConfig "github.com/initia-labs/minievm/x/evm/config"
evmconfig "github.com/initia-labs/minievm/x/evm/config"
evmkeeper "github.com/initia-labs/minievm/x/evm/keeper"
evmtypes "github.com/initia-labs/minievm/x/evm/types"
)
Expand All @@ -62,6 +63,7 @@ var ModuleBasics = module.NewBasicManager(
bank.AppModuleBasic{},
ibchooks.AppModuleBasic{},
evm.AppModuleBasic{},
ibc.AppModuleBasic{},
)

var (
Expand Down Expand Up @@ -277,6 +279,7 @@ func _createTestInput(
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
ac,
)
ibcHooksKeeper.Params.Set(ctx, ibchookstypes.DefaultParams())

communityPoolKeeper := &MockCommunityPoolKeeper{}
evmKeeper := evmkeeper.NewKeeper(
Expand All @@ -286,7 +289,7 @@ func _createTestInput(
accountKeeper,
communityPoolKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
EVMConfig.DefaultEVMConfig(),
evmconfig.DefaultEVMConfig(),
)
evmParams := evmtypes.DefaultParams()
require.NoError(t, evmKeeper.Params.Set(ctx, evmParams))
Expand All @@ -299,7 +302,7 @@ func _createTestInput(
// ibc middleware setup

mockIBCMiddleware := mockIBCMiddleware{}
evmHooks := evmhooks.NewEVMHooks(evmKeeper, ac)
evmHooks := evmhooks.NewEVMHooks(appCodec, ac, evmKeeper)

middleware := ibchooks.NewICS4Middleware(mockIBCMiddleware, evmHooks)
ibcHookMiddleware := ibchooks.NewIBCMiddleware(mockIBCMiddleware, middleware, ibcHooksKeeper)
Expand Down
9 changes: 6 additions & 3 deletions app/ibc-hooks/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package evm_hooks

import (
"cosmossdk.io/core/address"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported"
Expand All @@ -20,20 +21,22 @@ var (
)

type EVMHooks struct {
evmKeeper *evmkeeper.Keeper
codec codec.Codec
ac address.Codec
evmKeeper *evmkeeper.Keeper
asyncCallbackABI *abi.ABI
}

func NewEVMHooks(evmKeeper *evmkeeper.Keeper, ac address.Codec) *EVMHooks {
func NewEVMHooks(codec codec.Codec, ac address.Codec, evmKeeper *evmkeeper.Keeper) *EVMHooks {
abi, err := i_ibc_async_callback.IIbcAsyncCallbackMetaData.GetAbi()
if err != nil {
panic(err)
}

return &EVMHooks{
evmKeeper: evmKeeper,
codec: codec,
ac: ac,
evmKeeper: evmKeeper,
asyncCallbackABI: abi,
}
}
Expand Down
3 changes: 1 addition & 2 deletions app/ibc-hooks/receive.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (h EVMHooks) onRecvIcs20Packet(
if allowed, err := h.checkACL(im, ctx, msg.ContractAddr); err != nil {
return newEmitErrorAcknowledgement(err)
} else if !allowed {
return im.App.OnRecvPacket(ctx, packet, relayer)
return newEmitErrorAcknowledgement(fmt.Errorf("contract `%s` not allowed to be used in ibchooks", msg.ContractAddr))
}

// Validate whether the receiver is correctly specified or not.
Expand Down Expand Up @@ -63,7 +63,6 @@ func (h EVMHooks) onRecvIcs20Packet(
return ack
}

fmt.Println("SIBONG")
msg.Sender = intermediateSender
_, err = h.execMsg(ctx, msg)
if err != nil {
Expand Down
2 changes: 0 additions & 2 deletions app/ibc-hooks/receive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ func Test_onReceiveIcs20Packet_noMemo(t *testing.T) {
_, _, addr := keyPubAddr()
_, _, addr2 := keyPubAddr()

// don't have any memo
data := transfertypes.FungibleTokenPacketData{
Denom: "foo",
Amount: "10000",
Expand Down Expand Up @@ -58,7 +57,6 @@ func Test_onReceiveIcs20Packet_memo(t *testing.T) {
inputBz, err := abi.Pack("increase")
require.NoError(t, err)

// don't have any memo
data := transfertypes.FungibleTokenPacketData{
Denom: "foo",
Amount: "10000",
Expand Down
1 change: 1 addition & 0 deletions app/ibc-hooks/timeout.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func (h EVMHooks) onTimeoutIcs20Packet(
if allowed, err := h.checkACL(im, ctx, callback.ContractAddress); err != nil {
return err
} else if !allowed {
// just return nil here to avoid packet stuck due to hook acl.
return nil
}

Expand Down
100 changes: 100 additions & 0 deletions app/ibc-hooks/timeout_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package evm_hooks_test

import (
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"testing"

transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
"github.com/initia-labs/minievm/x/evm/contracts/counter"
"github.com/stretchr/testify/require"
)

func Test_onTimeoutIcs20Packet_noMemo(t *testing.T) {
ctx, input := createDefaultTestInput(t)
_, _, addr := keyPubAddr()
_, _, addr2 := keyPubAddr()

data := transfertypes.FungibleTokenPacketData{
Denom: "foo",
Amount: "10000",
Sender: addr.String(),
Receiver: addr2.String(),
Memo: "",
}

dataBz, err := json.Marshal(&data)
require.NoError(t, err)

err = input.IBCHooksMiddleware.OnTimeoutPacket(ctx, channeltypes.Packet{
Data: dataBz,
}, addr)
require.NoError(t, err)
}

func Test_onTimeoutIcs20Packet_memo(t *testing.T) {
ctx, input := createDefaultTestInput(t)
_, _, addr := keyPubAddr()
evmAddr := common.BytesToAddress(addr.Bytes())

codeBz, err := hex.DecodeString(strings.TrimPrefix(counter.CounterBin, "0x"))
require.NoError(t, err)

_, contractAddr, err := input.EVMKeeper.EVMCreate(ctx, evmAddr, codeBz)
require.NoError(t, err)

abi, err := counter.CounterMetaData.GetAbi()
require.NoError(t, err)

data := transfertypes.FungibleTokenPacketData{
Denom: "foo",
Amount: "10000",
Sender: addr.String(),
Receiver: contractAddr.Hex(),
Memo: fmt.Sprintf(`{
"evm": {
"async_callback": {
"id": 99,
"contract_address": "%s"
}
}
}`, contractAddr.Hex()),
}

dataBz, err := json.Marshal(&data)
require.NoError(t, err)

// hook should not be called to due to acl
err = input.IBCHooksMiddleware.OnTimeoutPacket(ctx, channeltypes.Packet{
Data: dataBz,
}, addr)
require.NoError(t, err)

// check the contract state
queryInputBz, err := abi.Pack("count")
require.NoError(t, err)
queryRes, logs, err := input.EVMKeeper.EVMCall(ctx, evmAddr, contractAddr, queryInputBz)
require.NoError(t, err)
require.Equal(t, uint256.NewInt(0).Bytes32(), [32]byte(queryRes))
require.Empty(t, logs)

// set acl
require.NoError(t, input.IBCHooksKeeper.SetAllowed(ctx, contractAddr[:], true))

// success
err = input.IBCHooksMiddleware.OnTimeoutPacket(ctx, channeltypes.Packet{
Data: dataBz,
}, addr)
require.NoError(t, err)

// check the contract state; increased by 99
queryRes, logs, err = input.EVMKeeper.EVMCall(ctx, evmAddr, contractAddr, queryInputBz)
require.NoError(t, err)
require.Equal(t, uint256.NewInt(99).Bytes32(), [32]byte(queryRes))
require.Empty(t, logs)
}
9 changes: 5 additions & 4 deletions app/ibc-hooks/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"cosmossdk.io/errors"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"

Expand Down Expand Up @@ -124,11 +125,11 @@ func newEmitErrorAcknowledgement(err error) channeltypes.Acknowledgement {
}

// isAckError checks an IBC acknowledgement to see if it's an error.
// This is a replacement for ack.Success() which is currently not working on some circumstances
func isAckError(acknowledgement []byte) bool {
var ackErr channeltypes.Acknowledgement_Error
if err := json.Unmarshal(acknowledgement, &ackErr); err == nil && len(ackErr.Error) > 0 {
func isAckError(appCodec codec.Codec, acknowledgement []byte) bool {
var ack channeltypes.Acknowledgement
if err := appCodec.UnmarshalJSON(acknowledgement, &ack); err == nil && !ack.Success() {
return true
}

return false
}
Loading

0 comments on commit 41af3f1

Please sign in to comment.