Skip to content

Commit

Permalink
feat(e2e): added transfer test (#15)
Browse files Browse the repository at this point in the history
Co-authored-by: Gjermund Garaba <[email protected]>
  • Loading branch information
srdtrk and gjermundgaraba authored Aug 5, 2024
1 parent e8a5294 commit 6861e8a
Show file tree
Hide file tree
Showing 18 changed files with 411 additions and 149 deletions.
1 change: 1 addition & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
test:
# List your tests here
- TestWithIbcEurekaTestSuite/TestDeploy
- TestWithIbcEurekaTestSuite/TestICS20Transfer
name: ${{ matrix.test }}
runs-on: ubuntu-latest
steps:
Expand Down
20 changes: 10 additions & 10 deletions abi/ICS20Transfer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -103,8 +103,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -176,8 +176,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -243,8 +243,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -345,8 +345,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "memo",
Expand Down
36 changes: 18 additions & 18 deletions abi/ICS26Router.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -218,8 +218,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -319,8 +319,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "version",
Expand Down Expand Up @@ -360,8 +360,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -453,8 +453,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -552,8 +552,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -607,8 +607,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -662,8 +662,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down Expand Up @@ -717,8 +717,8 @@
},
{
"name": "timeoutTimestamp",
"type": "uint32",
"internalType": "uint32"
"type": "uint64",
"internalType": "uint64"
},
{
"name": "sourcePort",
Expand Down
Binary file modified e2e/artifacts/darwin-aarch64/operator
Binary file not shown.
8 changes: 4 additions & 4 deletions e2e/artifacts/genesis.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"trustedClientState": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000012754500000000000000000000000000000000000000000000000000000000001baf800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000673696d642d310000000000000000000000000000000000000000000000000000",
"trustedConsensusState": "0000000000000000000000000000000000000000000000000000000066acb767c22394cd988169e4cff51cf02eee69039b119d7b3959ce598de9adf8de4524014ae23e439252e05b6db14d268bff60e595ec7666772339b013b449e5e7f0e00e",
"updateClientVkey": "0x008d1636cb723e4319abda291c18f7dd68e37c2703f6da3733c3a998bfa039f3",
"membershipVkey": "0x0014aa880aba41e680e859af6ebdefef52df9902ab61975bd1bb340c9b820b2a",
"ucAndMembershipVkey": "0x001d0fed4a58085ccac826775bfe0553ae6e42e52521aa52c3601396854dc4e8"
"trustedConsensusState": "0000000000000000000000000000000000000000000000000000000066b05d264416d86ca61c0fd693df00a5c755d74acdf8f9d5fd3f88dfa98df9dc9a08f906e2d69a8e2501adc2013515c2954fc617365a7d4e1a51bc49ca6bac597971949c",
"updateClientVkey": "0x0068b9d316aced51c5923b2d50692f4a6a9bfefcd89392914b90e77545727fbe",
"membershipVkey": "0x00a4245d249b5c35c9782cc899c8e370a35d5d928187dc9e7acbab7096764b72",
"ucAndMembershipVkey": "0x00cea834e3408d45d29080a3146e4fb1fd0c06503d655bd787219caac86cf59c"
}
Binary file modified e2e/artifacts/linux-x86_64/operator
Binary file not shown.
170 changes: 168 additions & 2 deletions e2e/interchaintestv8/ibc_eureka_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"os"
"strconv"
"strings"
Expand All @@ -14,11 +15,17 @@ import (
"github.com/stretchr/testify/suite"

ethcommon "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"

banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"

transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types"
ibchost "github.com/cosmos/ibc-go/v8/modules/core/24-host"
ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported"
mock "github.com/cosmos/ibc-go/v8/modules/light-clients/00-mock"
ibctesting "github.com/cosmos/ibc-go/v8/testing"
Expand All @@ -42,7 +49,9 @@ type IbcEurekaTestSuite struct {
e2esuite.TestSuite

// The private key of a test account
key *ecdsa.PrivateKey
key *ecdsa.PrivateKey
// The private key of the faucet account of interchaintest
faucet *ecdsa.PrivateKey
deployer ibc.Wallet

contractAddresses e2esuite.DeployedContracts
Expand Down Expand Up @@ -79,6 +88,10 @@ func (s *IbcEurekaTestSuite) SetupSuite(ctx context.Context) {
s.Require().NoError(err)
operatorAddress := crypto.PubkeyToAddress(operatorKey.PublicKey).Hex()

// get faucet private key from string
s.faucet, err = crypto.HexToECDSA(testvalues.FaucetPrivateKey)
s.Require().NoError(err)

os.Setenv(testvalues.EnvKeyEthRPC, eth.GetHostRPCAddress())
os.Setenv(testvalues.EnvKeyTendermintRPC, simd.GetHostRPCAddress())
os.Setenv(testvalues.EnvKeySp1Prover, "network")
Expand Down Expand Up @@ -133,9 +146,13 @@ func (s *IbcEurekaTestSuite) SetupSuite(ctx context.Context) {
s.Require().NoError(err)
s.erc20Contract, err = erc20.NewContract(ethcommon.HexToAddress(s.contractAddresses.Erc20), client)
s.Require().NoError(err)
}))

_, err = ethclient.Dial(eth.GetHostRPCAddress())
s.Require().True(s.Run("Fund address with ERC20", func() {
tx, err := s.erc20Contract.Transfer(s.GetTransactOpts(s.faucet), crypto.PubkeyToAddress(s.key.PublicKey), big.NewInt(testvalues.StartingTokenAmount))
s.Require().NoError(err)

_ = s.GetTxReciept(ctx, eth, tx.Hash()) // wait for the tx to be mined
}))

_, simdRelayerUser := s.GetRelayerUsers(ctx)
Expand Down Expand Up @@ -259,5 +276,154 @@ func (s *IbcEurekaTestSuite) TestDeploy() {
s.Require().NoError(err)
s.Require().Equal(s.contractAddresses.Ics20Transfer, strings.ToLower(transferAddress.Hex()))
}))

s.Require().True(s.Run("Verify ERC20 Genesis", func() {
userBalance, err := s.erc20Contract.BalanceOf(nil, crypto.PubkeyToAddress(s.key.PublicKey))
s.Require().NoError(err)
s.Require().Equal(testvalues.StartingTokenAmount, userBalance.Int64())
}))
}))
}

func (s *IbcEurekaTestSuite) TestICS20Transfer() {
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
var recvAck []byte

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(30 * time.Minute).UnixNano())
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)
}))

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

s.Require().True(s.Run("recvPacket on Cosmos side", func() {
resp, err := e2esuite.GRPCQuery[clienttypes.QueryClientStateResponse](ctx, simd, &clienttypes.QueryClientStateRequest{
ClientId: s.simdClientID,
})
s.Require().NoError(err)
var clientState mock.ClientState
err = simd.Config().EncodingConfig.Codec.Unmarshal(resp.ClientState.Value, &clientState)
s.Require().NoError(err)

txResp, err := s.BroadcastMessages(ctx, simd, s.UserB, 200_000, &channeltypes.MsgRecvPacket{
Packet: channeltypes.Packet{
Sequence: uint64(packet.Sequence),
SourcePort: packet.SourcePort,
SourceChannel: packet.SourceChannel,
DestinationPort: packet.DestPort,
DestinationChannel: packet.DestChannel,
Data: packet.Data,
TimeoutHeight: clienttypes.Height{},
TimeoutTimestamp: packet.TimeoutTimestamp,
},
ProofCommitment: []byte("doesn't matter"),
ProofHeight: clientState.LatestHeight,
Signer: s.UserB.FormattedAddress(),
})
s.Require().NoError(err)

recvAck, err = ibctesting.ParseAckFromEvents(txResp.Events)
s.Require().NoError(err)
s.Require().NotNil(recvAck)

s.Require().True(s.Run("Verify balances", func() {
ibcDenom := transfertypes.ParseDenomTrace(
fmt.Sprintf("%s/%s/%s", transfertypes.PortID, "00-mock-0", s.contractAddresses.Erc20),
).IBCDenom()

// Check the balance of UserB
resp, err := e2esuite.GRPCQuery[banktypes.QueryBalanceResponse](ctx, simd, &banktypes.QueryBalanceRequest{
Address: s.UserB.FormattedAddress(),
Denom: ibcDenom,
})
s.Require().NoError(err)
s.Require().NotNil(resp.Balance)
s.Require().Equal(testvalues.TransferAmount, resp.Balance.Amount.Int64())
s.Require().Equal(ibcDenom, resp.Balance.Denom)
}))
}))

s.True(s.Run("acknowledgePacket on Ethereum side", 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
packetAckPath := ibchost.PacketAcknowledgementPath(packet.DestPort, packet.DestChannel, uint64(packet.Sequence))
proofHeight, ucAndMemProof, err := operator.UpdateClientAndMembershipProof(
uint64(trustedHeight), uint64(latestHeight), packetAckPath,
"--trust-level", testvalues.DefaultTrustLevel.String(),
"--trusting-period", strconv.Itoa(testvalues.DefaultTrustPeriod),
)
s.Require().NoError(err)

msg := ics26router.IICS26RouterMsgsMsgAckPacket{
Packet: packet,
Acknowledgement: recvAck,
ProofAcked: ucAndMemProof,
ProofHeight: *proofHeight,
}

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

receipt := s.GetTxReciept(ctx, eth, tx.Hash())
s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status)
}))
}
Loading

0 comments on commit 6861e8a

Please sign in to comment.