Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions e2e/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ func (d *deployCLI) sendIFT(t *testing.T, route e2etest.Route, ift, to, amount s
require.NoErrorf(t, json.Unmarshal(stdout, &send), "tx ift send stdout: %s", stdout)

router := d.manifest(t, chainID).Core.Router
block, sequence := sentPacket(ctx, t, chain.RPCURL(), router, send.TxHash)
block, sequence := sentPacket(ctx, t, chain.RPCURL(), router, d.clientID(), send.TxHash)
return e2etest.PacketTx{
RouteID: route.ID,
Source: route.Source,
Expand Down Expand Up @@ -612,16 +612,16 @@ func assertPublicRelaying(ctx context.Context, t testing.TB, rpcURL string, m de
}

// sentPacket returns the block and sequence of the one packet the router
// sent in txHash.
func sentPacket(ctx context.Context, t testing.TB, rpcURL, router, txHash string) (uint64, uint64) {
// sent over clientID in txHash.
func sentPacket(ctx context.Context, t testing.TB, rpcURL, router, clientID, txHash string) (uint64, uint64) {
t.Helper()
client, err := ethclient.DialContext(ctx, rpcURL)
require.NoError(t, err)
defer client.Close()
receipt, err := client.TransactionReceipt(ctx, common.HexToHash(txHash))
require.NoError(t, err)
sequences, err := e2etest.SendPacketSequences(common.HexToAddress(router), receipt)
packets, err := e2etest.SentPackets(common.HexToAddress(router), clientID, receipt)
require.NoError(t, err)
require.Lenf(t, sequences, 1, "SendPacket events in %s", txHash)
return receipt.BlockNumber.Uint64(), sequences[0]
require.Lenf(t, packets, 1, "packets sent over client %q in %s", clientID, txHash)
return receipt.BlockNumber.Uint64(), packets[0].Sequence
}
16 changes: 8 additions & 8 deletions e2e/ift_rate_limit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ func TestIFTTransfer_RecvRateLimitRefund(t *testing.T) {
require.NoError(t, err)

// Both receives write acknowledgements, including the rejected transfer.
sequences, err := iftApp.WriteAcknowledgementSequences(ctx, recvHash)
ids, err := iftApp.WriteAcknowledgementPacketIDs(ctx, recvHash)
require.NoError(t, err)
require.ElementsMatch(t, []uint64{packets[0].Sequence, packets[1].Sequence}, sequences)
require.ElementsMatch(t, []e2etest.PacketID{packets[0].ID(), packets[1].ID()}, ids)

// Prove the rejected transfer hit the limit, rather than an unrelated application error.
parser, err := ics26router.NewContractFilterer(apps.ICS26Router, nil)
Expand Down Expand Up @@ -153,11 +153,11 @@ func testIFTRefundRateLimit(t *testing.T, timeout time.Duration) {
// Tighten the source limit after sending so only refunds, not outbound burns, are constrained.
setIFTRateLimit(t, source, deployment, sender)
terminal := relayerv2.PacketState_PACKET_STATE_REJECTED
sequencesInTx := iftApp.AckPacketSequences
packetIDsInTx := iftApp.AckPacketIDs
refundTx := (*relayerv2.PacketStatus).GetAckTx
if timeout != 0 {
terminal = relayerv2.PacketState_PACKET_STATE_TIMED_OUT
sequencesInTx = iftApp.TimeoutPacketSequences
packetIDsInTx = iftApp.TimeoutPacketIDs
refundTx = (*relayerv2.PacketStatus).GetTimeoutTx
requireTimeoutFinalized(t, env, route.Destination, batch.LatestTimeoutTimestamp())
}
Expand Down Expand Up @@ -211,9 +211,9 @@ func testIFTRefundRateLimit(t *testing.T, timeout time.Duration) {
require.Equal(t, [][]byte{limitError[:4]}, failures)

// Only the successful refund should emit an ack or timeout event.
sequences, err := sequencesInTx(ctx, firstHash)
ids, err := packetIDsInTx(ctx, firstHash)
require.NoError(t, err)
require.Equal(t, []uint64{packets[refunded].Sequence}, sequences,
require.Equal(t, []e2etest.PacketID{packets[refunded].ID()}, ids,
"only the refund that fits may succeed")

// The blocked refund stays pending, and balances reflect just one refund.
Expand All @@ -230,9 +230,9 @@ func testIFTRefundRateLimit(t *testing.T, timeout time.Duration) {
require.NoError(t, err)
retryHash := refundTx(retried).GetTxHash()
require.NotEqual(t, firstHash, retryHash)
sequences, err = sequencesInTx(ctx, retryHash)
ids, err = packetIDsInTx(ctx, retryHash)
require.NoError(t, err)
require.Equal(t, []uint64{packets[pending].Sequence}, sequences)
require.Equal(t, []e2etest.PacketID{packets[pending].ID()}, ids)
_, err = e2etest.AwaitState(ctx, relayer, packets[refunded], terminal)
require.NoError(t, err)
require.NoError(t, batch.VerifyState(ctx, nil, []int{0, 1}))
Expand Down
54 changes: 27 additions & 27 deletions e2e/ift_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func TestIFTTimeout_RefundUsesFinalizedDestinationAnchor(t *testing.T) {
destinationAnchor := destinationHeight - 1
anchorHeader, err := destinationEVM.HeaderByNumber(ctx, new(big.Int).SetUint64(destinationAnchor))
require.NoError(t, err)
require.Greater(t, anchorHeader.Time, asymmetricTransfer.TimeoutTimestamp())
require.Greater(t, anchorHeader.Time, asymmetricTransfer.Packet().TimeoutTimestamp)

require.NoError(t, destinationMining.WithPaused(ctx, func() error {
require.NoError(t, sourceMining.Mine(ctx, 50))
Expand Down Expand Up @@ -375,7 +375,7 @@ func TestIFTTimeout_Refund(t *testing.T) {
require.NoError(t, transfer.VerifyBurned(ctx))
require.NoError(t, transfer.VerifyPending(ctx))

requireTimeoutFinalized(t, env, route.Destination, transfer.TimeoutTimestamp())
requireTimeoutFinalized(t, env, route.Destination, transfer.Packet().TimeoutTimestamp)

require.NoError(t, e2etest.RelayAll(ctx, relayer, transfer.PacketTx()))
_, err = e2etest.AwaitState(ctx, relayer, transfer.PacketTx(),
Expand Down Expand Up @@ -416,7 +416,7 @@ func TestIFTTimeout_WaitsForFinality(t *testing.T) {
require.NoError(t, transfer.VerifyBurned(ctx))
require.NoError(t, transfer.VerifyPending(ctx))

timeout := time.Unix(int64(transfer.TimeoutTimestamp()), 0) //nolint:gosec // EVM timestamps fit in int64
timeout := time.Unix(int64(transfer.Packet().TimeoutTimestamp), 0) //nolint:gosec // EVM timestamps fit in int64
require.Eventually(t, func() bool { return time.Now().After(timeout) },
destination.Timing().CompletionBudget, destination.Timing().PollInterval,
"wall clock must pass the packet timeout while destination mining is paused")
Expand All @@ -429,7 +429,7 @@ func TestIFTTimeout_WaitsForFinality(t *testing.T) {
new(big.Int).SetUint64(height-finalityOffset),
)
require.NoError(t, headerErr)
require.Less(t, finalizedHeader.Time, transfer.TimeoutTimestamp(),
require.Less(t, finalizedHeader.Time, transfer.Packet().TimeoutTimestamp,
"finalized destination header must remain before the packet timeout")

require.NoError(t, e2etest.RelayAll(ctx, relayer, transfer.PacketTx()))
Expand All @@ -453,7 +453,7 @@ func TestIFTTimeout_WaitsForFinality(t *testing.T) {
if !assert.NoError(collect, headerErr) {
return
}
assert.Greater(collect, finalizedHeader.Time, transfer.TimeoutTimestamp(),
assert.Greater(collect, finalizedHeader.Time, transfer.Packet().TimeoutTimestamp,
"finalized destination header must pass the packet timeout")
}, destination.Timing().CompletionBudget, destination.Timing().PollInterval)

Expand Down Expand Up @@ -837,36 +837,36 @@ func TestIFTTransfer_BatchedRecvAck(t *testing.T) {
require.Equal(t, send.PacketTx().Sequence, statuses[0].SequenceNumber)
}

recvTxSequences := make(map[string][]uint64, packetCount)
ackTxSequences := make(map[string][]uint64, packetCount)
recvTxPackets := make(map[string][]e2etest.PacketID, packetCount)
ackTxPackets := make(map[string][]e2etest.PacketID, packetCount)
for _, send := range sends {
status, err := e2etest.AwaitState(ctx, relayer, send.PacketTx(),
relayerv2.PacketState_PACKET_STATE_SUCCEEDED)
require.NoError(t, err)
require.NoError(t, send.VerifyDelivered(ctx))

sequence := send.PacketTx().Sequence
id := send.PacketTx().ID()
recvHash := status.GetRecvTx().GetTxHash()
ackHash := status.GetAckTx().GetTxHash()
recvTxSequences[recvHash] = append(recvTxSequences[recvHash], sequence)
ackTxSequences[ackHash] = append(ackTxSequences[ackHash], sequence)
recvTxPackets[recvHash] = append(recvTxPackets[recvHash], id)
ackTxPackets[ackHash] = append(ackTxPackets[ackHash], id)
}
require.NoError(t, sends[packetCount-1].VerifyBurned(ctx), "successful acks must not refund")

for txHash, want := range recvTxSequences {
got, err := iftApp.WriteAcknowledgementSequences(ctx, txHash)
for txHash, want := range recvTxPackets {
got, err := iftApp.WriteAcknowledgementPacketIDs(ctx, txHash)
require.NoError(t, err)
require.ElementsMatchf(t, want, got, "receive transaction %s packet sequences", txHash)
require.ElementsMatchf(t, want, got, "receive transaction %s packets", txHash)
}
for txHash, want := range ackTxSequences {
got, err := iftApp.AckPacketSequences(ctx, txHash)
for txHash, want := range ackTxPackets {
got, err := iftApp.AckPacketIDs(ctx, txHash)
require.NoError(t, err)
require.ElementsMatchf(t, want, got, "acknowledgement transaction %s packet sequences", txHash)
require.ElementsMatchf(t, want, got, "acknowledgement transaction %s packets", txHash)
}
require.Lessf(t, len(recvTxSequences), packetCount,
"expected batched recv, got one recv tx per packet: %v", recvTxSequences)
require.Lessf(t, len(ackTxSequences), packetCount,
"expected batched ack, got one ack tx per packet: %v", ackTxSequences)
require.Lessf(t, len(recvTxPackets), packetCount,
"expected batched recv, got one recv tx per packet: %v", recvTxPackets)
require.Lessf(t, len(ackTxPackets), packetCount,
"expected batched ack, got one ack tx per packet: %v", ackTxPackets)
}

// TestIFTTransfer_BatchedTimeout sends more IFT transfers
Expand Down Expand Up @@ -917,22 +917,22 @@ func TestIFTTransfer_BatchedTimeout(t *testing.T) {
require.NoErrorf(t, err, "relay call for packet %d (seq %d) failed", i, sends[i].PacketTx().Sequence)
}

timeoutTxSequences := make(map[string][]uint64, packetCount)
timeoutTxPackets := make(map[string][]e2etest.PacketID, packetCount)
for _, send := range sends {
status, err := e2etest.AwaitState(ctx, relayer, send.PacketTx(),
relayerv2.PacketState_PACKET_STATE_TIMED_OUT)
require.NoError(t, err)
require.NoError(t, send.VerifyNotMinted(ctx))

txHash := status.GetTimeoutTx().GetTxHash()
timeoutTxSequences[txHash] = append(timeoutTxSequences[txHash], send.PacketTx().Sequence)
timeoutTxPackets[txHash] = append(timeoutTxPackets[txHash], send.PacketTx().ID())
}

for txHash, want := range timeoutTxSequences {
got, err := iftApp.TimeoutPacketSequences(ctx, txHash)
for txHash, want := range timeoutTxPackets {
got, err := iftApp.TimeoutPacketIDs(ctx, txHash)
require.NoError(t, err)
require.ElementsMatchf(t, want, got, "timeout transaction %s packet sequences", txHash)
require.ElementsMatchf(t, want, got, "timeout transaction %s packets", txHash)
}
require.Lessf(t, len(timeoutTxSequences), packetCount,
"expected batched timeouts, got one timeout tx per packet: %v", timeoutTxSequences)
require.Lessf(t, len(timeoutTxPackets), packetCount,
"expected batched timeouts, got one timeout tx per packet: %v", timeoutTxPackets)
}
102 changes: 40 additions & 62 deletions e2e/internal/e2etest/traffic.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
package e2etest

import (
"errors"
"fmt"
"slices"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"

"github.com/cosmos/ibc-contracts/packages/go-abigen/ics26router"
"github.com/cosmos/ibc/e2e/internal/harness/chain/evm"
"github.com/cosmos/ibc/e2e/internal/harness/environment"
)

Expand All @@ -30,84 +32,60 @@ func (p PacketTx) String() string {
return fmt.Sprintf("%s-%d", p.RouteID, p.Sequence)
}

// ID identifies the packet among all packets the source Chain sends.
func (p PacketTx) ID() PacketID {
return PacketID{SourceClientID: p.SourceClientID, Sequence: p.Sequence}
}

// emittedBy checks that ids, read from txHash's events of the given kind,
// include this packet.
func (p PacketTx) emittedBy(ids []PacketID, event, txHash string) error {
if !slices.Contains(ids, p.ID()) {
return fmt.Errorf("e2etest: transaction %s emitted no %s event for packet %s", txHash, event, p.reference())
}
return nil
}

func (p PacketTx) reference() string {
return fmt.Sprintf("route %q sequence %d", p.RouteID, p.Sequence)
}

// PacketID identifies a packet on its source Chain: a sequence is only unique
// per source client.
type PacketID struct {
SourceClientID string
Sequence uint64
}

// sendResult is what every application send produces: the locator of the packet
// it emitted and the source transaction receipt that emitted it. The
// application send types embed it so they all expose the same handles.
// it emitted and the packet itself. The application send types embed it so they
// all expose the same handles.
type sendResult struct {
packetTx PacketTx
receipt *types.Receipt
packet ics26router.IICS26RouterMsgsPacket
}

// newSendResult pairs a source transaction receipt with one packet it emitted.
func newSendResult(
routeID RouteID,
source endpoint,
sourceClientID string,
receipt *types.Receipt,
sequence uint64,
) sendResult {
return sendResult{
packetTx: PacketTx{
RouteID: routeID,
Source: source.chain.ID(),
SourceClientID: sourceClientID,
SourceTxHash: receipt.TxHash.Hex(),
SourceBlockNumber: receipt.BlockNumber.Uint64(),
Sequence: sequence,
},
receipt: receipt,
}
// sourceRoute is the source side every application sends from: the signer on
// the source Chain and the router and client its packets leave through.
type sourceRoute struct {
routeID RouteID
source endpoint
sender evm.Account
sourceRouter common.Address
sourceClientID string
}

// PacketTx locates the packet this send emitted.
func (s sendResult) PacketTx() PacketTx { return s.packetTx }

// Packet is the packet this send emitted, as the source router's SendPacket
// event carried it.
func (s sendResult) Packet() ics26router.IICS26RouterMsgsPacket { return s.packet }

// TxHash is the hex hash of the source transaction that emitted the packet.
func (s sendResult) TxHash() string { return s.packetTx.SourceTxHash }

// Receipt is the source transaction receipt, for assertions the typed helpers
// do not cover.
func (s sendResult) Receipt() *types.Receipt { return s.receipt }

type endpoint struct {
chain *environment.Chain
evm *environment.EVM
}

func resolveRouteEndpoints(
routeID RouteID,
source, destination *environment.Chain,
) (endpoint, endpoint, error) {
if routeID == "" {
return endpoint{}, endpoint{}, errors.New("e2etest: route id is required")
}
if source == nil {
return endpoint{}, endpoint{}, errors.New("e2etest: source Chain is required")
}
if destination == nil {
return endpoint{}, endpoint{}, errors.New("e2etest: destination Chain is required")
}
if source.ID() == destination.ID() {
return endpoint{}, endpoint{}, fmt.Errorf(
"e2etest: route %q must connect different Chains",
routeID,
)
}
sourceEVM, err := source.EVM()
if err != nil {
return endpoint{}, endpoint{}, fmt.Errorf("e2etest: source Chain %q: %w", source.ID(), err)
}
destinationEVM, err := destination.EVM()
if err != nil {
return endpoint{}, endpoint{}, fmt.Errorf(
"e2etest: destination Chain %q: %w",
destination.ID(),
err,
)
}
return endpoint{chain: source, evm: sourceEVM}, endpoint{chain: destination, evm: destinationEVM}, nil
}
Loading
Loading