Skip to content

Commit

Permalink
Add ibc-reflect test
Browse files Browse the repository at this point in the history
  • Loading branch information
chipshort committed May 8, 2024
1 parent 7831f6f commit 0d39415
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 0 deletions.
88 changes: 88 additions & 0 deletions x/wasm/ibc_integration_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package wasm_test

import (
"encoding/base64"
"encoding/json"
"fmt"
"testing"

wasmvm "github.com/CosmWasm/wasmvm/v2"
Expand Down Expand Up @@ -248,6 +250,92 @@ func TestOnIBCPacketReceive(t *testing.T) {
}
}

func TestIBCAsyncAck(t *testing.T) {
// given 2 chains with a mock on chain A to control the IBC flow
// and the ibc-reflect contract on chain B
// when the no_ack package is relayed
// then the contract does not produce an ack
// and
// when the async_ack message is executed on chain B
// then the contract produces the ack

mockContractEngine := NewCaptureAckTestContractEngine()
chainAOpts := []wasmkeeper.Option{
wasmkeeper.WithWasmEngine(mockContractEngine),
}
var (
coord = wasmibctesting.NewCoordinator(t, 2, chainAOpts)
chainA = coord.GetChain(wasmibctesting.GetChainID(1))
chainB = coord.GetChain(wasmibctesting.GetChainID(2))
)
// setup chain A contract metadata for mock
myMockContractAddr := chainA.SeedNewContractInstance() // setups env but uses mock contract

// setup chain B contracts
reflectID := chainB.StoreCodeFile("./keeper/testdata/reflect_1_5.wasm").CodeID
initMsg, err := json.Marshal(wasmkeeper.IBCReflectInitMsg{ReflectCodeID: reflectID})
require.NoError(t, err)
codeID := chainB.StoreCodeFile("./keeper/testdata/ibc_reflect.wasm").CodeID
ibcReflectContractAddr := chainB.InstantiateContract(codeID, initMsg)

// establish IBC channels
var (
sourcePortID = chainA.ContractInfo(myMockContractAddr).IBCPortID
counterpartPortID = chainB.ContractInfo(ibcReflectContractAddr).IBCPortID
path = wasmibctesting.NewPath(chainA, chainB)
)
path.EndpointA.ChannelConfig = &ibctesting.ChannelConfig{
PortID: sourcePortID, Version: "ibc-reflect-v1", Order: channeltypes.UNORDERED,
}
path.EndpointB.ChannelConfig = &ibctesting.ChannelConfig{
PortID: counterpartPortID, Version: "ibc-reflect-v1", Order: channeltypes.UNORDERED,
}

coord.SetupConnections(path)
coord.CreateChannels(path)
coord.CommitBlock(chainA, chainB)
require.Equal(t, 0, len(chainA.PendingSendPackets))
require.Equal(t, 0, len(chainB.PendingSendPackets))

// when the "no_ack" ibc packet is sent from chain A to chain B
capturedAck := mockContractEngine.SubmitIBCPacket(t, path, chainA, myMockContractAddr, []byte(`{"no_ack":{}}`))
coord.CommitBlock(chainA, chainB)

require.Equal(t, 1, len(chainA.PendingSendPackets))
require.Equal(t, 0, len(chainB.PendingSendPackets))

// we don't expect an ack yet
err = path.RelayPacketWithoutAck(chainA.PendingSendPackets[0], nil)
noAckPacket := chainA.PendingSendPackets[0]
chainA.PendingSendPackets = []channeltypes.Packet{}
require.NoError(t, err)
assert.Nil(t, *capturedAck)

// when the "async_ack" ibc packet is sent from chain A to chain B
destChannel := path.EndpointB.ChannelID
packetSeq := 1
ackData := base64.StdEncoding.EncodeToString([]byte("my ack"))
ack := fmt.Sprintf(`{"data":"%s", "success": true}`, ackData)
msg := fmt.Sprintf(`{"async_ack":{"channel_id":"%s","packet_sequence": "%d", "ack": %s}}`, destChannel, packetSeq, ack)
res, err := chainB.SendMsgs(&types.MsgExecuteContract{
Sender: chainB.SenderAccount.GetAddress().String(),
Contract: ibcReflectContractAddr.String(),
Msg: []byte(msg),
})
require.NoError(t, err)

// relay the ack
err = path.EndpointA.UpdateClient()
require.NoError(t, err)
acknowledgement, err := wasmibctesting.ParseAckFromEvents(res.GetEvents())
require.NoError(t, err)
err = path.EndpointA.AcknowledgePacket(noAckPacket, acknowledgement)
require.NoError(t, err)

// now ack for the no_ack packet should have arrived
require.Equal(t, []byte("my ack"), *capturedAck)
}

// mock to submit an ibc data package from given chain and capture the ack
type captureAckTestContractEngine struct {
*wasmtesting.MockWasmEngine
Expand Down
51 changes: 51 additions & 0 deletions x/wasm/ibctesting/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,57 @@ func (path *Path) RelayPacket(packet channeltypes.Packet, _ []byte) error {
return fmt.Errorf("packet commitment does not exist on either endpoint for provided packet")
}

// RelayPacketWithoutAck attempts to relay the packet first on EndpointA and then on EndpointB
// if EndpointA does not contain a packet commitment for that packet. An error is returned
// if a relay step fails or the packet commitment does not exist on either endpoint.
// In contrast to RelayPacket, this function does not acknowledge the packet and expects it to have no acknowledgement yet.
// It is useful for testing async acknowledgement.
func (path *Path) RelayPacketWithoutAck(packet channeltypes.Packet, _ []byte) error {
pc := path.EndpointA.Chain.App.GetIBCKeeper().ChannelKeeper.GetPacketCommitment(path.EndpointA.Chain.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
if bytes.Equal(pc, channeltypes.CommitPacket(path.EndpointA.Chain.App.AppCodec(), packet)) {

// packet found, relay from A to B
if err := path.EndpointB.UpdateClient(); err != nil {
return err
}

res, err := path.EndpointB.RecvPacketWithResult(packet)
if err != nil {
return err
}

_, err = ParseAckFromEvents(res.GetEvents())
if err == nil {
return fmt.Errorf("tried to relay packet without ack but got ack")
}

return nil
}

pc = path.EndpointB.Chain.App.GetIBCKeeper().ChannelKeeper.GetPacketCommitment(path.EndpointB.Chain.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
if bytes.Equal(pc, channeltypes.CommitPacket(path.EndpointB.Chain.App.AppCodec(), packet)) {

// packet found, relay B to A
if err := path.EndpointA.UpdateClient(); err != nil {
return err
}

res, err := path.EndpointA.RecvPacketWithResult(packet)
if err != nil {
return err
}

_, err = ParseAckFromEvents(res.GetEvents())
if err == nil {
return fmt.Errorf("tried to relay packet without ack but got ack")
}

return nil
}

return fmt.Errorf("packet commitment does not exist on either endpoint for provided packet")
}

// SendMsg delivers the provided messages to the chain. The counterparty
// client is updated with the new source consensus state.
func (path *Path) SendMsg(msgs ...sdk.Msg) error {
Expand Down

0 comments on commit 0d39415

Please sign in to comment.