mirror of https://github.com/certusone/wasmd.git
273 lines
10 KiB
Go
273 lines
10 KiB
Go
package e2e_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
wasmvmtypes "github.com/CosmWasm/wasmvm/v3/types"
|
|
ibctransfertypes "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types"
|
|
channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types"
|
|
ibctesting "github.com/cosmos/ibc-go/v10/testing"
|
|
mockv2 "github.com/cosmos/ibc-go/v10/testing/mock/v2"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
wasmibctesting "github.com/CosmWasm/wasmd/tests/wasmibctesting"
|
|
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
|
|
)
|
|
|
|
// QueryMsg is used to encode query messages to ibc2 contract
|
|
type QueryMsg struct {
|
|
QueryState struct{} `json:"query_state"`
|
|
}
|
|
|
|
// ibc2 contract response type
|
|
type State struct {
|
|
IBC2PacketAckCounter uint32 `json:"ibc2_packet_ack_counter"`
|
|
IBC2PacketReceiveCounter uint32 `json:"ibc2_packet_receive_counter"`
|
|
IBC2PacketTimeoutCounter uint32 `json:"ibc2_packet_timeout_counter"`
|
|
LastChannelID string `json:"last_channel_id"`
|
|
LastPacketSeq uint64 `json:"last_packet_seq"`
|
|
LastPacketSent *wasmvmtypes.IBC2PacketSendMsg `json:"last_packet_sent"`
|
|
}
|
|
|
|
// Message sent to the ibc2 contract over IBCv2 channel
|
|
type IbcPayload struct {
|
|
ResponseWithoutAck bool `json:"response_without_ack"`
|
|
SendAsyncAckForPrevMsg bool `json:"send_async_ack_for_prev_msg"`
|
|
}
|
|
|
|
type TestEnv struct {
|
|
contractPortA string
|
|
contractPortB string
|
|
contractAddrA sdk.AccAddress
|
|
contractAddrB sdk.AccAddress
|
|
path *wasmibctesting.WasmPath
|
|
chainA *wasmibctesting.WasmTestChain
|
|
chainB *wasmibctesting.WasmTestChain
|
|
coord *ibctesting.Coordinator
|
|
}
|
|
|
|
func setup(t *testing.T) TestEnv {
|
|
coord := wasmibctesting.NewCoordinator(t, 2)
|
|
chainA := wasmibctesting.NewWasmTestChain(coord.GetChain(ibctesting.GetChainID(1)))
|
|
chainB := wasmibctesting.NewWasmTestChain(coord.GetChain(ibctesting.GetChainID(2)))
|
|
contractCodeA := chainA.StoreCodeFile("./testdata/ibc2.wasm").CodeID
|
|
contractAddrA := chainA.InstantiateContract(contractCodeA, []byte(`{}`))
|
|
contractPortA := wasmkeeper.PortIDForContractV2(contractAddrA)
|
|
require.NotEmpty(t, contractAddrA)
|
|
|
|
contractCodeB := chainB.StoreCodeFile("./testdata/ibc2.wasm").CodeID
|
|
// Skip initial contract address to not overlap with ChainA
|
|
_ = chainB.InstantiateContract(contractCodeB, []byte(`{}`))
|
|
contractAddrB := chainB.InstantiateContract(contractCodeB, []byte(`{}`))
|
|
contractPortB := wasmkeeper.PortIDForContractV2(contractAddrB)
|
|
require.NotEmpty(t, contractAddrB)
|
|
|
|
path := wasmibctesting.NewWasmPath(chainA, chainB)
|
|
path.EndpointA.ChannelConfig = &ibctesting.ChannelConfig{
|
|
PortID: contractPortA,
|
|
Version: ibctransfertypes.V1,
|
|
Order: channeltypes.UNORDERED,
|
|
}
|
|
path.EndpointB.ChannelConfig = &ibctesting.ChannelConfig{
|
|
PortID: contractPortB,
|
|
Version: ibctransfertypes.V1,
|
|
Order: channeltypes.UNORDERED,
|
|
}
|
|
|
|
path.Path.SetupV2()
|
|
|
|
return TestEnv{
|
|
contractPortA: contractPortA,
|
|
contractPortB: contractPortB,
|
|
contractAddrA: contractAddrA,
|
|
contractAddrB: contractAddrB,
|
|
path: path,
|
|
chainA: chainA,
|
|
chainB: chainB,
|
|
coord: coord,
|
|
}
|
|
}
|
|
|
|
func TestIBC2AckMsg(t *testing.T) {
|
|
testEnv := setup(t)
|
|
|
|
// IBC v2 Payload from contract on Chain B to contract on Chain A
|
|
payload := mockv2.NewMockPayload(testEnv.contractPortB, testEnv.contractPortA)
|
|
var err error
|
|
payload.Value, err = json.Marshal(IbcPayload{ResponseWithoutAck: false, SendAsyncAckForPrevMsg: false})
|
|
require.NoError(t, err)
|
|
|
|
// Message timeout
|
|
timeoutTimestamp := uint64(testEnv.chainB.GetContext().BlockTime().Add(time.Minute * 5).Unix())
|
|
|
|
_, err = testEnv.path.EndpointB.MsgSendPacket(timeoutTimestamp, payload)
|
|
require.NoError(t, err)
|
|
|
|
// First message send through test
|
|
err = wasmibctesting.RelayPendingPacketsWithAcksV2(testEnv.path)
|
|
require.NoError(t, err)
|
|
|
|
// Check if counter was incremented in the recv entry point
|
|
var response State
|
|
|
|
err = testEnv.chainB.SmartQuery(testEnv.contractAddrB.String(), QueryMsg{QueryState: struct{}{}}, &response)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint32(1), response.IBC2PacketAckCounter)
|
|
}
|
|
|
|
func TestIBC2SendReceiveMsg(t *testing.T) {
|
|
testEnv := setup(t)
|
|
|
|
// IBC v2 Payload from contract on Chain B to contract on Chain A
|
|
payload := mockv2.NewMockPayload(testEnv.contractPortB, testEnv.contractPortA)
|
|
var err error
|
|
payload.Value, err = json.Marshal(IbcPayload{ResponseWithoutAck: false, SendAsyncAckForPrevMsg: false})
|
|
require.NoError(t, err)
|
|
|
|
// Message timeout
|
|
timeoutTimestamp := uint64(testEnv.chainB.GetContext().BlockTime().Add(time.Minute * 5).Unix())
|
|
|
|
_, err = testEnv.path.EndpointB.MsgSendPacket(timeoutTimestamp, payload)
|
|
require.NoError(t, err)
|
|
|
|
// First message send through test
|
|
err = wasmibctesting.RelayPendingPacketsV2(testEnv.path)
|
|
require.NoError(t, err)
|
|
|
|
// Check if counter was incremented in the recv entry point
|
|
var response State
|
|
|
|
err = testEnv.chainA.SmartQuery(testEnv.contractAddrA.String(), QueryMsg{QueryState: struct{}{}}, &response)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint32(1), response.IBC2PacketReceiveCounter)
|
|
|
|
// The counters on both Chains are both incremented in every iteration of the loop,
|
|
// because once the first relaying loop in `RelayPendingPacketsV2` the array of
|
|
// pending packets on the other chain is updated with new packet send from the contract.
|
|
for i := 1; i <= 100; i++ {
|
|
// Relay message sent by contract
|
|
err = wasmibctesting.RelayPendingPacketsV2(testEnv.path)
|
|
require.NoError(t, err)
|
|
|
|
// Check counter in contract A
|
|
err = testEnv.chainA.SmartQuery(testEnv.contractAddrA.String(), QueryMsg{QueryState: struct{}{}}, &response)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint32(i+1), response.IBC2PacketReceiveCounter)
|
|
|
|
// Check counter in contract B
|
|
err = testEnv.chainB.SmartQuery(testEnv.contractAddrB.String(), QueryMsg{QueryState: struct{}{}}, &response)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint32(i), response.IBC2PacketReceiveCounter)
|
|
}
|
|
}
|
|
|
|
func TestIBC2RAsyncAckSending(t *testing.T) {
|
|
testEnv := setup(t)
|
|
|
|
var err error
|
|
timeoutTimestamp := testEnv.chainA.GetTimeoutTimestampSecs()
|
|
payload := mockv2.NewMockPayload(testEnv.contractPortB, testEnv.contractPortA)
|
|
payload.Value, err = json.Marshal(IbcPayload{ResponseWithoutAck: true})
|
|
require.NoError(t, err)
|
|
packetToAck, err := testEnv.path.EndpointB.MsgSendPacket(timeoutTimestamp, payload)
|
|
require.NoError(t, err)
|
|
err = testEnv.path.EndpointA.MsgRecvPacket(packetToAck)
|
|
require.NoError(t, err)
|
|
|
|
timeoutTimestamp = testEnv.chainA.GetTimeoutTimestampSecs()
|
|
payload = mockv2.NewMockPayload(testEnv.contractPortB, testEnv.contractPortA)
|
|
payload.Value, err = json.Marshal(IbcPayload{SendAsyncAckForPrevMsg: true})
|
|
require.NoError(t, err)
|
|
packet, err := testEnv.path.EndpointB.MsgSendPacket(timeoutTimestamp, payload)
|
|
require.NoError(t, err)
|
|
|
|
// Check that the ACK was not sent at this moment
|
|
var response State
|
|
err = testEnv.chainB.SmartQuery(testEnv.contractAddrB.String(), QueryMsg{QueryState: struct{}{}}, &response)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint32(0), response.IBC2PacketAckCounter)
|
|
|
|
// Relay the second packet and expect an ACK sent for the first packet
|
|
err = wasmibctesting.RelayPacketV2(testEnv.path, packet, testEnv.path.EndpointB, testEnv.path.EndpointA, &packetToAck)
|
|
require.NoError(t, err)
|
|
|
|
// Check if counter was incremented in the recv entry point
|
|
err = testEnv.chainB.SmartQuery(testEnv.contractAddrB.String(), QueryMsg{QueryState: struct{}{}}, &response)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint32(1), response.IBC2PacketAckCounter)
|
|
}
|
|
|
|
func TestIBC2TimeoutMsg(t *testing.T) {
|
|
testEnv := setup(t)
|
|
|
|
// IBC v2 Payload from contract on Chain B to contract on Chain A
|
|
payload := mockv2.NewMockPayload(testEnv.contractPortB, testEnv.contractPortA)
|
|
var err error
|
|
payload.Value, err = json.Marshal(IbcPayload{ResponseWithoutAck: false, SendAsyncAckForPrevMsg: false})
|
|
require.NoError(t, err)
|
|
|
|
// Message timeout
|
|
timeoutTimestamp := uint64(testEnv.chainB.GetContext().BlockTime().Add(time.Minute).Unix())
|
|
|
|
_, err = testEnv.path.EndpointB.MsgSendPacket(timeoutTimestamp, payload)
|
|
require.NoError(t, err)
|
|
|
|
// First message send through test.
|
|
// Contract replies back with the IbcPayload response.
|
|
err = wasmibctesting.RelayPendingPacketsV2(testEnv.path)
|
|
require.NoError(t, err)
|
|
|
|
// Send timeout on the packet sent by the contract
|
|
err = wasmibctesting.TimeoutPendingPacketsV2(testEnv.coord, testEnv.path)
|
|
require.NoError(t, err)
|
|
|
|
// Check that timeout message was sent to the contract
|
|
var response State
|
|
err = testEnv.chainA.SmartQuery(testEnv.contractAddrA.String(), QueryMsg{QueryState: struct{}{}}, &response)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint32(1), response.IBC2PacketTimeoutCounter)
|
|
}
|
|
|
|
func TestIBC2SendMsg(t *testing.T) {
|
|
testEnv := setup(t)
|
|
|
|
// IBC v2 Payload from contract on Chain B to contract on Chain A
|
|
payload := mockv2.NewMockPayload(testEnv.contractPortB, testEnv.contractPortA)
|
|
var err error
|
|
payload.Value, err = json.Marshal(IbcPayload{ResponseWithoutAck: false, SendAsyncAckForPrevMsg: false})
|
|
require.NoError(t, err)
|
|
|
|
// Message timeout
|
|
timeoutTimestamp := uint64(testEnv.chainB.GetContext().BlockTime().Add(time.Minute).Unix())
|
|
|
|
_, err = testEnv.path.EndpointB.MsgSendPacket(timeoutTimestamp, payload)
|
|
require.NoError(t, err)
|
|
|
|
// First message send through test.
|
|
// Contract replies back with the IbcPayload response.
|
|
err = wasmibctesting.RelayPendingPacketsV2(testEnv.path)
|
|
require.NoError(t, err)
|
|
|
|
// Check that send message was sent to the contract
|
|
var response State
|
|
err = testEnv.chainA.SmartQuery(testEnv.contractAddrA.String(), QueryMsg{QueryState: struct{}{}}, &response)
|
|
require.NoError(t, err)
|
|
require.Equal(t, &wasmvmtypes.IBC2PacketSendMsg{
|
|
Payload: wasmvmtypes.IBC2Payload{
|
|
SourcePort: wasmkeeper.PortIDForContractV2(testEnv.contractAddrA),
|
|
DestinationPort: wasmkeeper.PortIDForContractV2(testEnv.contractAddrB),
|
|
Version: "mock-version",
|
|
Encoding: "application/x-protobuf",
|
|
Value: []byte(`{"response_without_ack":false,"send_async_ack_for_prev_msg":false}`),
|
|
},
|
|
SourceClient: "07-tendermint-0",
|
|
DestinationClient: "07-tendermint-0",
|
|
PacketSequence: 1,
|
|
Signer: testEnv.contractAddrA.String(),
|
|
}, response.LastPacketSent)
|
|
}
|