Skip to content

Commit

Permalink
feat(e2e): added timeout tests and switch timestamps to seconds (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
srdtrk authored Aug 5, 2024
1 parent 6861e8a commit 1993aed
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 18 deletions.
1 change: 1 addition & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jobs:
# List your tests here
- TestWithIbcEurekaTestSuite/TestDeploy
- TestWithIbcEurekaTestSuite/TestICS20Transfer
- TestWithIbcEurekaTestSuite/TestICS20Timeout
name: ${{ matrix.test }}
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 1 addition & 1 deletion e2e/artifacts/genesis.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000012754500000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000673696d642d310000000000000000000000000000000000000000000000000000",
"trustedConsensusState": "0000000000000000000000000000000000000000000000000000000066b05d264416d86ca61c0fd693df00a5c755d74acdf8f9d5fd3f88dfa98df9dc9a08f906e2d69a8e2501adc2013515c2954fc617365a7d4e1a51bc49ca6bac597971949c",
"trustedConsensusState": "0000000000000000000000000000000000000000000000000000000066b0b92862093024debc13c1fab2cce33d04895624033edca5cc22f7266085eca4c6461606c79e13ccd4e467cae775cbc073ccca2735947069c0255e78337c50dcdee823",
"updateClientVkey": "0x0068b9d316aced51c5923b2d50692f4a6a9bfefcd89392914b90e77545727fbe",
"membershipVkey": "0x00a4245d249b5c35c9782cc899c8e370a35d5d928187dc9e7acbab7096764b72",
"ucAndMembershipVkey": "0x00cea834e3408d45d29080a3146e4fb1fd0c06503d655bd787219caac86cf59c"
Expand Down
105 changes: 100 additions & 5 deletions e2e/interchaintestv8/ibc_eureka_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,6 @@ func (s *IbcEurekaTestSuite) TestICS20Transfer() {
transferAmount := big.NewInt(testvalues.TransferAmount)
userAddress := crypto.PubkeyToAddress(s.key.PublicKey)
receiver := s.UserB
var packet ics26router.IICS26RouterMsgsPacket
var recvAck []byte

s.Require().True(s.Run("Approve the ICS20Transfer contract to spend the erc20 tokens", func() {
ics20Address := ethcommon.HexToAddress(s.contractAddresses.Ics20Transfer)
Expand All @@ -310,8 +308,9 @@ func (s *IbcEurekaTestSuite) TestICS20Transfer() {
s.Require().Equal(transferAmount, allowance)
}))

var packet ics26router.IICS26RouterMsgsPacket
s.Require().True(s.Run("sendTransfer on Ethereum side", func() {
timeout := uint64(time.Now().Add(30 * time.Minute).UnixNano())
timeout := uint64(time.Now().Add(30 * time.Minute).Unix())
msgSendTransfer := ics20transfer.IICS20TransferMsgsSendTransferMsg{
Denom: s.contractAddresses.Erc20,
Amount: transferAmount,
Expand Down Expand Up @@ -349,6 +348,7 @@ func (s *IbcEurekaTestSuite) TestICS20Transfer() {

// TODO: When using a non-mock light client on the cosmos side, the client there needs to be updated at this point

var recvAck []byte
s.Require().True(s.Run("recvPacket on Cosmos side", func() {
resp, err := e2esuite.GRPCQuery[clienttypes.QueryClientStateResponse](ctx, simd, &clienttypes.QueryClientStateRequest{
ClientId: s.simdClientID,
Expand All @@ -367,7 +367,7 @@ func (s *IbcEurekaTestSuite) TestICS20Transfer() {
DestinationChannel: packet.DestChannel,
Data: packet.Data,
TimeoutHeight: clienttypes.Height{},
TimeoutTimestamp: packet.TimeoutTimestamp,
TimeoutTimestamp: packet.TimeoutTimestamp * 1_000_000_000,
},
ProofCommitment: []byte("doesn't matter"),
ProofHeight: clientState.LatestHeight,
Expand Down Expand Up @@ -404,7 +404,7 @@ func (s *IbcEurekaTestSuite) TestICS20Transfer() {
latestHeight, err := simd.Height(ctx)
s.Require().NoError(err)

// This will be a non-membership proof since no packets have been sent
// This will be a membership proof since the acknowledgement is written
packetAckPath := ibchost.PacketAcknowledgementPath(packet.DestPort, packet.DestChannel, uint64(packet.Sequence))
proofHeight, ucAndMemProof, err := operator.UpdateClientAndMembershipProof(
uint64(trustedHeight), uint64(latestHeight), packetAckPath,
Expand All @@ -427,3 +427,98 @@ func (s *IbcEurekaTestSuite) TestICS20Transfer() {
s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status)
}))
}

func (s *IbcEurekaTestSuite) TestICS20Timeout() {
ctx := context.Background()

s.SetupSuite(ctx)

eth, simd := s.ChainA, s.ChainB

transferAmount := big.NewInt(testvalues.TransferAmount)
userAddress := crypto.PubkeyToAddress(s.key.PublicKey)
receiver := s.UserB

var packet ics26router.IICS26RouterMsgsPacket
s.Require().True(s.Run("Approve the ICS20Transfer contract to spend the erc20 tokens", func() {
ics20Address := ethcommon.HexToAddress(s.contractAddresses.Ics20Transfer)
tx, err := s.erc20Contract.Approve(s.GetTransactOpts(s.key), ics20Address, transferAmount)
s.Require().NoError(err)
receipt := s.GetTxReciept(ctx, eth, tx.Hash())
s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status)

allowance, err := s.erc20Contract.Allowance(nil, userAddress, ics20Address)
s.Require().NoError(err)
s.Require().Equal(transferAmount, allowance)
}))

s.Require().True(s.Run("sendTransfer on Ethereum side", func() {
timeout := uint64(time.Now().Add(1 * time.Minute).Unix())
msgSendTransfer := ics20transfer.IICS20TransferMsgsSendTransferMsg{
Denom: s.contractAddresses.Erc20,
Amount: transferAmount,
Receiver: receiver.FormattedAddress(),
SourceChannel: s.ethClientID,
DestPort: "transfer",
TimeoutTimestamp: timeout,
Memo: "testmemo",
}

tx, err := s.ics20Contract.SendTransfer(s.GetTransactOpts(s.key), msgSendTransfer)
s.Require().NoError(err)
receipt := s.GetTxReciept(ctx, eth, tx.Hash())
s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status)

transferEvent, err := e2esuite.GetEvmEvent(receipt, s.ics20Contract.ParseICS20Transfer)
s.Require().NoError(err)
s.Require().Equal(s.contractAddresses.Erc20, strings.ToLower(transferEvent.PacketData.Erc20ContractAddress.Hex()))
s.Require().Equal(transferAmount, transferEvent.PacketData.Amount)
s.Require().Equal(userAddress, transferEvent.PacketData.Sender)
s.Require().Equal(receiver.FormattedAddress(), transferEvent.PacketData.Receiver)
s.Require().Equal("testmemo", transferEvent.PacketData.Memo)

sendPacketEvent, err := e2esuite.GetEvmEvent(receipt, s.ics26Contract.ParseSendPacket)
s.Require().NoError(err)
packet = sendPacketEvent.Packet
s.Require().Equal(uint32(1), packet.Sequence)
s.Require().Equal(timeout, packet.TimeoutTimestamp)
s.Require().Equal("transfer", packet.SourcePort)
s.Require().Equal(s.ethClientID, packet.SourceChannel)
s.Require().Equal("transfer", packet.DestPort)
s.Require().Equal(s.simdClientID, packet.DestChannel)
s.Require().Equal(transfertypes.Version, packet.Version)
}))

// sleep for 61 seconds to let the packet timeout
time.Sleep(61 * time.Second)

s.True(s.Run("timeoutPacket on Ethereum", func() {
clientState, err := s.sp1Ics07Contract.GetClientState(nil)
s.Require().NoError(err)

trustedHeight := clientState.LatestHeight.RevisionHeight
latestHeight, err := simd.Height(ctx)
s.Require().NoError(err)

// This will be a non-membership proof since no packets have been sent
packetReceiptPath := ibchost.PacketReceiptPath(packet.DestPort, packet.DestChannel, uint64(packet.Sequence))
proofHeight, ucAndMemProof, err := operator.UpdateClientAndMembershipProof(
uint64(trustedHeight), uint64(latestHeight), packetReceiptPath,
"--trust-level", testvalues.DefaultTrustLevel.String(),
"--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod),
)
s.Require().NoError(err)

msg := ics26router.IICS26RouterMsgsMsgTimeoutPacket{
Packet: packet,
ProofTimeout: ucAndMemProof,
ProofHeight: *proofHeight,
}

tx, err := s.ics26Contract.TimeoutPacket(s.GetTransactOpts(s.key), msg)
s.Require().NoError(err)

receipt := s.GetTxReciept(ctx, eth, tx.Hash())
s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status)
}))
}
10 changes: 4 additions & 6 deletions src/ICS26Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,8 @@ contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors, Ree
string memory counterpartyId = ics02Client.getCounterparty(msg_.sourceChannel).clientId;

// TODO: validate all identifiers
uint64 nanoTimestamp = uint64(block.timestamp * 1_000_000_000);
if (msg_.timeoutTimestamp <= nanoTimestamp) {
revert IBCInvalidTimeoutTimestamp(msg_.timeoutTimestamp, nanoTimestamp);
if (msg_.timeoutTimestamp <= block.timestamp) {
revert IBCInvalidTimeoutTimestamp(msg_.timeoutTimestamp, block.timestamp);
}

uint32 sequence = IBCStore.nextSequenceSend(msg_.sourcePort, msg_.sourceChannel);
Expand Down Expand Up @@ -125,9 +124,8 @@ contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors, Ree
});

ics02Client.getClient(msg_.packet.destChannel).membership(membershipMsg);
uint64 nanoTimestamp = uint64(block.timestamp * 1_000_000_000);
if (msg_.packet.timeoutTimestamp <= nanoTimestamp) {
revert IBCInvalidTimeoutTimestamp(msg_.packet.timeoutTimestamp, nanoTimestamp);
if (msg_.packet.timeoutTimestamp <= block.timestamp) {
revert IBCInvalidTimeoutTimestamp(msg_.packet.timeoutTimestamp, block.timestamp);
}

bytes memory ack =
Expand Down
2 changes: 1 addition & 1 deletion src/msgs/IICS20TransferMsgs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface IICS20TransferMsgs {
string sourceChannel;
/// The destination port on the counterparty chain
string destPort;
/// The absolute timeout timestamp in unix nano seconds
/// The absolute timeout timestamp in unix seconds
uint64 timeoutTimestamp;
/// Optional memo
string memo;
Expand Down
3 changes: 2 additions & 1 deletion src/utils/ICS24Host.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.8.25;

import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { IICS26RouterMsgs } from "../msgs/IICS26RouterMsgs.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";

// @title ICS24 Host Path Generators
// @notice ICS24Host is a library that provides commitment path generators for ICS24 host requirements.
Expand Down Expand Up @@ -101,7 +102,7 @@ library ICS24Host {
function packetCommitmentBytes32(IICS26RouterMsgs.Packet memory packet) internal pure returns (bytes32) {
return sha256(
abi.encodePacked(
uint64(packet.timeoutTimestamp),
SafeCast.toUint64(uint256(packet.timeoutTimestamp) * 1_000_000_000),
uint64(0),
uint64(0),
sha256(packet.data),
Expand Down
7 changes: 3 additions & 4 deletions test/IntegrationTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,12 @@ contract IntegrationTest is Test {
sender = makeAddr("sender");
senderStr = Strings.toHexString(sender);
data = ICS20Lib.marshalJSON(erc20AddressStr, defaultAmount, senderStr, receiver, "memo");
uint64 nanoTimestamp = uint64((block.timestamp + 1000) * 1_000_000_000);
msgSendPacket = IICS26RouterMsgs.MsgSendPacket({
sourcePort: "transfer",
sourceChannel: clientIdentifier,
destPort: "transfer",
data: data,
timeoutTimestamp: nanoTimestamp,
timeoutTimestamp: uint64(block.timestamp + 1000),
version: ICS20Lib.ICS20_VERSION
});
}
Expand Down Expand Up @@ -166,7 +165,7 @@ contract IntegrationTest is Test {
IICS26RouterMsgs.Packet memory packet = _sendICS20Transfer();

// make light client return timestamp that is after our timeout
lightClient.setMembershipResult(msgSendPacket.timeoutTimestamp + uint64(1_000_000_000));
lightClient.setMembershipResult(msgSendPacket.timeoutTimestamp + 1);

IICS26RouterMsgs.MsgTimeoutPacket memory timeoutMsg = IICS26RouterMsgs.MsgTimeoutPacket({
packet: packet,
Expand Down Expand Up @@ -206,7 +205,7 @@ contract IntegrationTest is Test {
receiver: receiver,
sourceChannel: clientIdentifier,
destPort: "transfer",
timeoutTimestamp: uint64((block.timestamp + 1000) * 1_000_000_000),
timeoutTimestamp: uint64(block.timestamp + 1000),
memo: "memo"
});

Expand Down

0 comments on commit 1993aed

Please sign in to comment.