Skip to content

Commit

Permalink
CCIP-4509 Refactoring and improvements around token transfer and usdc…
Browse files Browse the repository at this point in the history
… transfer tests (#15502)

* Test

* Test

* Test

* Test

* Test

* Minor fixes

* Increasing timeout. Messages are sent much faster so we need to wait longer for the exec.

* Increasing timeout. Messages are sent much faster so we need to wait longer for the exec.

* Increasing timeout. Messages are sent much faster so we need to wait longer for the exec.

* Docs

* Docs
  • Loading branch information
mateusz-sekara authored Dec 6, 2024
1 parent 5e5a4cd commit 3e74229
Show file tree
Hide file tree
Showing 4 changed files with 335 additions and 192 deletions.
52 changes: 51 additions & 1 deletion deployment/ccip/changeset/test_assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,40 @@ func (c *commitReportTracker) allCommited(sourceChainSelector uint64) bool {
return true
}

// ConfirmMultipleCommits waits for multiple ccipocr3.SeqNumRange to be committed by the Offramp.
// Waiting is done in parallel per every sourceChain/destChain (lane) passed as argument.
func ConfirmMultipleCommits(
t *testing.T,
chains map[uint64]deployment.Chain,
state map[uint64]CCIPChainState,
startBlocks map[uint64]*uint64,
enforceSingleCommit bool,
expectedSeqNums map[SourceDestPair]ccipocr3.SeqNumRange,
) error {
errGrp := &errgroup.Group{}

for sourceDest, seqRange := range expectedSeqNums {
seqRange := seqRange
srcChain := sourceDest.SourceChainSelector
destChain := sourceDest.DestChainSelector

errGrp.Go(func() error {
_, err := ConfirmCommitWithExpectedSeqNumRange(
t,
chains[srcChain],
chains[destChain],
state[destChain].OffRamp,
startBlocks[destChain],
seqRange,
enforceSingleCommit,
)
return err
})
}

return errGrp.Wait()
}

// ConfirmCommitWithExpectedSeqNumRange waits for a commit report on the destination chain with the expected sequence number range.
// startBlock is the block number to start watching from.
// If startBlock is nil, it will start watching from the latest block.
Expand Down Expand Up @@ -449,7 +483,7 @@ func ConfirmExecWithSeqNrs(
return nil, fmt.Errorf("no expected sequence numbers provided")
}

timer := time.NewTimer(3 * time.Minute)
timer := time.NewTimer(8 * time.Minute)
defer timer.Stop()
tick := time.NewTicker(3 * time.Second)
defer tick.Stop()
Expand Down Expand Up @@ -564,6 +598,22 @@ func RequireConsistently(t *testing.T, condition func() bool, duration time.Dura
}
}

func SeqNumberRageToSlice(seqRanges map[SourceDestPair]ccipocr3.SeqNumRange) map[SourceDestPair][]uint64 {
flatten := make(map[SourceDestPair][]uint64)

for srcDst, seqRange := range seqRanges {
if _, ok := flatten[srcDst]; !ok {
flatten[srcDst] = make([]uint64, 0, seqRange.End()-seqRange.Start()+1)
}

for i := seqRange.Start(); i <= seqRange.End(); i++ {
flatten[srcDst] = append(flatten[srcDst], uint64(i))
}
}

return flatten
}

const (
EXECUTION_STATE_UNTOUCHED = 0
EXECUTION_STATE_INPROGRESS = 1
Expand Down
126 changes: 126 additions & 0 deletions deployment/ccip/changeset/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,78 @@ func Transfer(
return msgSentEvent, startBlocks
}

type TestTransferRequest struct {
Name string
SourceChain, DestChain uint64
Receiver common.Address
ExpectedStatus int
// optional
Tokens []router.ClientEVMTokenAmount
Data []byte
ExtraArgs []byte
ExpectedTokenBalances map[common.Address]*big.Int
}

// TransferMultiple sends multiple CCIPMessages (represented as TestTransferRequest) sequentially.
// It verifies whether message is not reverted on the source and proper event is emitted by OnRamp.
// However, it doesn't wait for message to be committed or executed. Therefore, you can send multiple messages very fast,
// but you need to make sure they are committed/executed on your own (if that's the intention).
// It saves some time during test execution, because we let plugins batch instead of executing one by one
// If you want to wait for execution in a "batch" manner you will need to pass maps returned by TransferMultiple to
// either ConfirmMultipleCommits (for commit) or ConfirmExecWithSeqNrsForAll (for exec). Check example usage in the tests.
func TransferMultiple(
ctx context.Context,
t *testing.T,
env deployment.Environment,
state CCIPOnChainState,
requests []TestTransferRequest,
) (
map[uint64]*uint64,
map[SourceDestPair]cciptypes.SeqNumRange,
map[SourceDestPair]map[uint64]int,
map[uint64]map[TokenReceiverIdentifier]*big.Int,
) {
startBlocks := make(map[uint64]*uint64)
expectedSeqNums := make(map[SourceDestPair]cciptypes.SeqNumRange)
expectedExecutionStates := make(map[SourceDestPair]map[uint64]int)
expectedTokenBalances := make(TokenBalanceAccumulator)

for _, tt := range requests {
t.Run(tt.Name, func(t *testing.T) {
expectedTokenBalances.add(tt.DestChain, tt.Receiver, tt.ExpectedTokenBalances)

pairId := SourceDestPair{
SourceChainSelector: tt.SourceChain,
DestChainSelector: tt.DestChain,
}

msg, blocks := Transfer(
ctx, t, env, state, tt.SourceChain, tt.DestChain, tt.Tokens, tt.Receiver, tt.Data, tt.ExtraArgs)
if _, ok := expectedExecutionStates[pairId]; !ok {
expectedExecutionStates[pairId] = make(map[uint64]int)
}
expectedExecutionStates[pairId][msg.SequenceNumber] = tt.ExpectedStatus

if _, ok := startBlocks[tt.DestChain]; !ok {
startBlocks[tt.DestChain] = blocks[tt.DestChain]
}

seqNr, ok := expectedSeqNums[pairId]
if ok {
expectedSeqNums[pairId] = cciptypes.NewSeqNumRange(
seqNr.Start(), cciptypes.SeqNum(msg.SequenceNumber),
)
} else {
expectedSeqNums[pairId] = cciptypes.NewSeqNumRange(
cciptypes.SeqNum(msg.SequenceNumber), cciptypes.SeqNum(msg.SequenceNumber),
)
}
})
}

return startBlocks, expectedSeqNums, expectedExecutionStates, expectedTokenBalances
}

// TransferAndWaitForSuccess sends a message from sourceChain to destChain and waits for it to be executed
func TransferAndWaitForSuccess(
ctx context.Context,
Expand Down Expand Up @@ -1258,6 +1330,60 @@ func TransferAndWaitForSuccess(
require.Equal(t, expectedStatus, states[identifier][msgSentEvent.SequenceNumber])
}

// TokenBalanceAccumulator is a convenient accumulator to aggregate expected balances of different tokens
// used across the tests. You can iterate over your test cases and build the final "expected" balances for tokens (per chain, per sender)
// For instance, if your test runs multiple transfers for the same token, and you want to verify the balance of tokens at
// the end of the execution, you can simply use that struct for aggregating expected tokens
// Please also see WaitForTokenBalances to better understand how you can assert token balances
type TokenBalanceAccumulator map[uint64]map[TokenReceiverIdentifier]*big.Int

func (t TokenBalanceAccumulator) add(
destChain uint64,
receiver common.Address,
expectedBalance map[common.Address]*big.Int) {
for token, balance := range expectedBalance {
tkIdentifier := TokenReceiverIdentifier{token, receiver}

if _, ok := t[destChain]; !ok {
t[destChain] = make(map[TokenReceiverIdentifier]*big.Int)
}
actual, ok := t[destChain][tkIdentifier]
if !ok {
actual = big.NewInt(0)
}
t[destChain][tkIdentifier] = new(big.Int).Add(actual, balance)
}
}

type TokenReceiverIdentifier struct {
token common.Address
receiver common.Address
}

// WaitForTokenBalances waits for multiple ERC20 tokens to reach a particular balance
// It works in a batch manner, so you can pass and exhaustive list of different tokens (per senders and chains)
// and it would work concurrently for the balance to be met. Check WaitForTheTokenBalance to see how balance
// checking is made for a token/receiver pair
func WaitForTokenBalances(
ctx context.Context,
t *testing.T,
chains map[uint64]deployment.Chain,
expectedBalances map[uint64]map[TokenReceiverIdentifier]*big.Int,
) {
errGrp := &errgroup.Group{}
for chainID, tokens := range expectedBalances {
for id, balance := range tokens {
id := id
balance := balance
errGrp.Go(func() error {
WaitForTheTokenBalance(ctx, t, id.token, id.receiver, chains[chainID], balance)
return nil
})
}
}
require.NoError(t, errGrp.Wait())
}

func WaitForTheTokenBalance(
ctx context.Context,
t *testing.T,
Expand Down
Loading

0 comments on commit 3e74229

Please sign in to comment.