From 00d966176b8212c277d3c62bd8b2b289db363862 Mon Sep 17 00:00:00 2001 From: Makram Date: Mon, 11 Nov 2024 19:10:08 +0400 Subject: [PATCH] deployment/ccip/changeset: add messaging test (#15166) * deployment/ccip/changeset: add messaging test Small refactor to the request helpers to allow for specifying all fields of the message. * fix lint * use cl-ccip version with fix * refactor test to dedup logic * combine var decls * fix ccip reader nonces test * bump cl-ccip to latest main --- .../ccipreader/ccipreader_test.go | 2 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- deployment/ccip/add_lane_test.go | 17 +- .../ccip/changeset/active_candidate_test.go | 10 +- deployment/ccip/changeset/add_chain_test.go | 8 +- .../ccip/changeset/initial_deploy_test.go | 10 +- deployment/ccip/changeset/messaging_test.go | 209 ++++++++++++++++++ deployment/ccip/deploy.go | 9 +- deployment/ccip/test_assertions.go | 18 +- deployment/ccip/test_helpers.go | 85 +++++-- deployment/go.mod | 2 +- deployment/go.sum | 4 +- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 +- integration-tests/smoke/ccip_rmn_test.go | 10 +- integration-tests/smoke/ccip_test.go | 30 ++- 21 files changed, 388 insertions(+), 50 deletions(-) create mode 100644 deployment/ccip/changeset/messaging_test.go diff --git a/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go b/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go index 39aa322e0a2..a992648a54f 100644 --- a/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go +++ b/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go @@ -439,7 +439,7 @@ func TestCCIPReader_Nonces(t *testing.T) { // Add some nonces. for chain, addrs := range nonces { for addr, nonce := range addrs { - _, err := s.contract.SetInboundNonce(s.auth, uint64(chain), nonce, addr.Bytes()) + _, err := s.contract.SetInboundNonce(s.auth, uint64(chain), nonce, common.LeftPadBytes(addr.Bytes(), 32)) assert.NoError(t, err) } } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 314e88a9b7f..c715a9d360a 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -288,7 +288,7 @@ require ( github.com/shirou/gopsutil/v3 v3.24.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 // indirect github.com/smartcontractkit/chain-selectors v1.0.27 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241018134907-a00ba3729b5e // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 5a30edb6c83..11e1a5d6b88 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1090,8 +1090,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 h1:BeLnOf2KKQpJj9nzfnE7QEg9ZqJ2jy/sbpNYVixVM2Y= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4 h1:e+uFsxQ21tMQKRu4oBXKycNzoR30vO/7STBtqtDvQJQ= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4/go.mod h1:ny87uTW6hLjCTLiBqBRNFEhETSXhHWevYlPclT5lSco= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= diff --git a/deployment/ccip/add_lane_test.go b/deployment/ccip/add_lane_test.go index d8443ad288b..223d978b814 100644 --- a/deployment/ccip/add_lane_test.go +++ b/deployment/ccip/add_lane_test.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -94,7 +95,13 @@ func TestAddLane(t *testing.T) { startBlock := latesthdr.Number.Uint64() // Send traffic on the first lane and it should not be processed by the plugin as onRamp is disabled // we will check this by confirming that the message is not executed by the end of the test - seqNum1 := TestSendRequest(t, e.Env, state, chain1, chain2, false, nil) + seqNum1 := TestSendRequest(t, e.Env, state, chain1, chain2, false, router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[chain2].Receiver.Address().Bytes(), 32), + Data: []byte("hello world"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }) require.Equal(t, uint64(1), seqNum1) // Add another lane @@ -104,7 +111,13 @@ func TestAddLane(t *testing.T) { latesthdr, err = e.Env.Chains[chain1].Client.HeaderByNumber(testcontext.Get(t), nil) require.NoError(t, err) startBlock2 := latesthdr.Number.Uint64() - seqNum2 := TestSendRequest(t, e.Env, state, chain2, chain1, false, nil) + seqNum2 := TestSendRequest(t, e.Env, state, chain2, chain1, false, router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[chain2].Receiver.Address().Bytes(), 32), + Data: []byte("hello world"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }) require.Equal(t, uint64(1), seqNum2) require.NoError(t, ConfirmExecWithSeqNr(t, e.Env.Chains[chain2], e.Env.Chains[chain1], state.Chains[chain1].OffRamp, &startBlock2, seqNum2)) diff --git a/deployment/ccip/changeset/active_candidate_test.go b/deployment/ccip/changeset/active_candidate_test.go index ab27d4c96db..9daf383c971 100644 --- a/deployment/ccip/changeset/active_candidate_test.go +++ b/deployment/ccip/changeset/active_candidate_test.go @@ -3,12 +3,14 @@ package changeset import ( "testing" + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/deployment" @@ -82,7 +84,13 @@ func TestActiveCandidate(t *testing.T) { require.NoError(t, err) block := latesthdr.Number.Uint64() startBlocks[dest] = &block - seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, nil) + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32), + Data: []byte("hello world"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }) expectedSeqNum[dest] = seqNum } } diff --git a/deployment/ccip/changeset/add_chain_test.go b/deployment/ccip/changeset/add_chain_test.go index c0d76875b6c..ff02430fd51 100644 --- a/deployment/ccip/changeset/add_chain_test.go +++ b/deployment/ccip/changeset/add_chain_test.go @@ -211,7 +211,13 @@ func TestAddChainInbound(t *testing.T) { latesthdr, err := e.Env.Chains[newChain].Client.HeaderByNumber(testcontext.Get(t), nil) require.NoError(t, err) startBlock := latesthdr.Number.Uint64() - seqNr := ccipdeployment.TestSendRequest(t, e.Env, state, initialDeploy[0], newChain, true, nil) + seqNr := ccipdeployment.TestSendRequest(t, e.Env, state, initialDeploy[0], newChain, true, router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[newChain].Receiver.Address().Bytes(), 32), + Data: []byte("hello world"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }) require.NoError(t, ccipdeployment.ConfirmCommitWithExpectedSeqNumRange(t, e.Env.Chains[initialDeploy[0]], e.Env.Chains[newChain], state.Chains[newChain].OffRamp, &startBlock, cciptypes.SeqNumRange{ cciptypes.SeqNum(1), diff --git a/deployment/ccip/changeset/initial_deploy_test.go b/deployment/ccip/changeset/initial_deploy_test.go index c172f9f84c8..e1adbf2050f 100644 --- a/deployment/ccip/changeset/initial_deploy_test.go +++ b/deployment/ccip/changeset/initial_deploy_test.go @@ -3,6 +3,7 @@ package changeset import ( "testing" + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink/deployment" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" @@ -11,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/stretchr/testify/require" @@ -72,7 +74,13 @@ func TestInitialDeploy(t *testing.T) { require.NoError(t, err) block := latesthdr.Number.Uint64() startBlocks[dest] = &block - seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, nil) + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32), + Data: []byte("hello"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }) expectedSeqNum[dest] = seqNum } } diff --git a/deployment/ccip/changeset/messaging_test.go b/deployment/ccip/changeset/messaging_test.go new file mode 100644 index 00000000000..a5fde58742b --- /dev/null +++ b/deployment/ccip/changeset/messaging_test.go @@ -0,0 +1,209 @@ +package changeset + +import ( + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/deployment" + ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/test-go/testify/require" + "golang.org/x/exp/maps" +) + +type testCaseSetup struct { + t *testing.T + sender []byte + deployedEnv ccipdeployment.DeployedEnv + onchainState ccipdeployment.CCIPOnChainState + sourceChain, destChain uint64 +} + +type messagingTestCase struct { + testCaseSetup + replayed bool + nonce uint64 +} + +type messagingTestCaseOutput struct { + replayed bool + nonce uint64 +} + +func Test_Messaging(t *testing.T) { + t.Parallel() + + // Setup 2 chains and a single lane. + e := ccipdeployment.NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 2, 4) + state, err := ccipdeployment.LoadOnchainState(e.Env) + require.NoError(t, err) + + allChainSelectors := maps.Keys(e.Env.Chains) + require.Len(t, allChainSelectors, 2) + sourceChain := allChainSelectors[0] + destChain := allChainSelectors[1] + t.Log("All chain selectors:", allChainSelectors, + ", home chain selector:", e.HomeChainSel, + ", feed chain selector:", e.FeedChainSel, + ", source chain selector:", sourceChain, + ", dest chain selector:", destChain, + ) + + tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) + newAddresses := deployment.NewMemoryAddressBook() + err = ccipdeployment.DeployCCIPContracts(e.Env, newAddresses, ccipdeployment.DeployCCIPContractConfig{ + HomeChainSel: e.HomeChainSel, + FeedChainSel: e.FeedChainSel, + ChainsToDeploy: allChainSelectors, + TokenConfig: tokenConfig, + MCMSConfig: ccipdeployment.NewTestMCMSConfig(t, e.Env), + OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + }) + require.NoError(t, err) + require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) + state, err = ccipdeployment.LoadOnchainState(e.Env) + require.NoError(t, err) + + // connect a single lane, source to dest + require.NoError(t, ccipdeployment.AddLane(e.Env, state, sourceChain, destChain)) + + var ( + replayed bool + nonce uint64 + sender = common.LeftPadBytes(e.Env.Chains[sourceChain].DeployerKey.From.Bytes(), 32) + out messagingTestCaseOutput + setup = testCaseSetup{ + t: t, + sender: sender, + deployedEnv: e, + onchainState: state, + sourceChain: sourceChain, + destChain: destChain, + } + ) + + t.Run("data message to eoa", func(t *testing.T) { + out = runMessagingTestCase(messagingTestCase{ + testCaseSetup: setup, + replayed: replayed, + nonce: nonce, + }, + common.HexToAddress("0xdead"), + []byte("hello eoa"), + ) + }) + + t.Run("message to contract not implementing CCIPReceiver", func(t *testing.T) { + out = runMessagingTestCase( + messagingTestCase{ + testCaseSetup: setup, + replayed: out.replayed, + nonce: out.nonce, + }, + state.Chains[destChain].FeeQuoter.Address(), + []byte("hello FeeQuoter"), + ) + }) + + t.Run("message to contract implementing CCIPReceiver", func(t *testing.T) { + out = runMessagingTestCase( + messagingTestCase{ + testCaseSetup: setup, + replayed: out.replayed, + nonce: out.nonce, + }, + state.Chains[destChain].Receiver.Address(), + []byte("hello CCIPReceiver"), + func(t *testing.T) { + iter, err := state.Chains[destChain].Receiver.FilterMessageReceived(nil) + require.NoError(t, err) + require.True(t, iter.Next()) + // MessageReceived doesn't emit the data unfortunately, so can't check that. + }, + ) + }) + + t.Run("message to contract implementing CCIPReceiver with low exec gas", func(t *testing.T) { + out = runMessagingTestCase( + messagingTestCase{ + testCaseSetup: setup, + replayed: out.replayed, + nonce: out.nonce, + }, + state.Chains[destChain].Receiver.Address(), + []byte("hello CCIPReceiver with low exec gas"), + func(t *testing.T) { + // Message should not be emitted, not enough gas to emit log. + // TODO: this is still returning a log, probably the older one since FAILURE is the execution state. + // Not enough ctx in the message received log to confirm that it's from another test. + // Maybe check the log block number and assert that its < the header before block number from above? + // iter, err := ccipReceiver.FilterMessageReceived(&bind.FilterOpts{ + // Start: headerBefore.Number.Uint64(), + // }) + // require.NoError(t, err) + // require.False(t, iter.Next(), "MessageReceived should not be emitted in this test case since gas is too low") + }, + ) + }) +} + +func sleepAndReplay(t *testing.T, e ccipdeployment.DeployedEnv, sourceChain, destChain uint64) { + time.Sleep(30 * time.Second) + replayBlocks := make(map[uint64]uint64) + replayBlocks[sourceChain] = 1 + replayBlocks[destChain] = 1 + ccipdeployment.ReplayLogs(t, e.Env.Offchain, replayBlocks) +} + +func runMessagingTestCase( + tc messagingTestCase, + receiver common.Address, + msgData []byte, + extraAssertions ...func(t *testing.T), +) (out messagingTestCaseOutput) { + // check latest nonce + latestNonce, err := tc.onchainState.Chains[tc.destChain].NonceManager.GetInboundNonce(&bind.CallOpts{ + Context: tests.Context(tc.t), + }, tc.sourceChain, tc.sender) + require.NoError(tc.t, err) + require.Equal(tc.t, tc.nonce, latestNonce) + + startBlocks := make(map[uint64]*uint64) + seqNum := ccipdeployment.TestSendRequest(tc.t, tc.deployedEnv.Env, tc.onchainState, tc.sourceChain, tc.destChain, false, router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(receiver.Bytes(), 32), + Data: msgData, + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }) + expectedSeqNum := make(map[uint64]uint64) + expectedSeqNum[tc.destChain] = seqNum + + // hack + if !tc.replayed { + sleepAndReplay(tc.t, tc.deployedEnv, tc.sourceChain, tc.destChain) + out.replayed = true + } + + ccipdeployment.ConfirmCommitForAllWithExpectedSeqNums(tc.t, tc.deployedEnv.Env, tc.onchainState, expectedSeqNum, startBlocks) + ccipdeployment.ConfirmExecWithSeqNrForAll(tc.t, tc.deployedEnv.Env, tc.onchainState, expectedSeqNum, startBlocks) + + // check the sender latestNonce on the dest, should be incremented + latestNonce, err = tc.onchainState.Chains[tc.destChain].NonceManager.GetInboundNonce(&bind.CallOpts{ + Context: tests.Context(tc.t), + }, tc.sourceChain, tc.sender) + require.NoError(tc.t, err) + require.Equal(tc.t, tc.nonce+1, latestNonce) + out.nonce = latestNonce + tc.t.Logf("confirmed nonce bump for sender %x, latestNonce %d", tc.sender, latestNonce) + + for _, assertion := range extraAssertions { + assertion(tc.t) + } + + return +} diff --git a/deployment/ccip/deploy.go b/deployment/ccip/deploy.go index f407b9856c4..77df3aab60f 100644 --- a/deployment/ccip/deploy.go +++ b/deployment/ccip/deploy.go @@ -573,8 +573,15 @@ func DeployChainContracts( tx, err = tokenAdminRegistry.Contract.AddRegistryModule(chain.DeployerKey, customRegistryModule.Address) if err != nil { e.Logger.Errorw("Failed to assign registry module on token admin registry", "err", err) - return err + return fmt.Errorf("failed to assign registry module on token admin registry: %w", err) } + + _, err = chain.Confirm(tx) + if err != nil { + e.Logger.Errorw("Failed to confirm assign registry module on token admin registry", "err", err) + return fmt.Errorf("failed to confirm assign registry module on token admin registry: %w", err) + } + e.Logger.Infow("assigned registry module on token admin registry") nonceManager, err := deployContract(e.Logger, chain, ab, diff --git a/deployment/ccip/test_assertions.go b/deployment/ccip/test_assertions.go index d1389fc5ce3..26c0d93c8a0 100644 --- a/deployment/ccip/test_assertions.go +++ b/deployment/ccip/test_assertions.go @@ -134,7 +134,7 @@ func ConfirmTokenPriceUpdated( } if len(tokenToInitialPrice) > 0 { - return fmt.Errorf("Not all tokens updated on chain %d", chain.Selector) + return fmt.Errorf("not all tokens updated on chain %d", chain.Selector) } return nil @@ -272,7 +272,7 @@ func ConfirmCommitWithExpectedSeqNumRange( } // ConfirmExecWithSeqNrForAll waits for all chains in the environment to execute the given expectedSeqNums. -// expectedSeqNums is a map of destinationchain selector to expected sequence number +// expectedSeqNums is a map of destination chain selector to expected sequence number // startBlocks is a map of destination chain selector to start block number to start watching from. // If startBlocks is nil, it will start watching from the latest block. func ConfirmExecWithSeqNrForAll( @@ -343,17 +343,17 @@ func ConfirmExecWithSeqNr( scc, executionState := GetExecutionState(t, source, dest, offRamp, expectedSeqNr) t.Logf("Waiting for ExecutionStateChanged on chain %d (offramp %s) from chain %d with expected sequence number %d, current onchain minSeqNr: %d, execution state: %s", dest.Selector, offRamp.Address().String(), source.Selector, expectedSeqNr, scc.MinSeqNr, executionStateToString(executionState)) - if executionState == EXECUTION_STATE_SUCCESS { - t.Logf("Observed SUCCESS execution state on chain %d (offramp %s) from chain %d with expected sequence number %d", - dest.Selector, offRamp.Address().String(), source.Selector, expectedSeqNr) + if executionState == EXECUTION_STATE_SUCCESS || executionState == EXECUTION_STATE_FAILURE { + t.Logf("Observed %s execution state on chain %d (offramp %s) from chain %d with expected sequence number %d", + executionStateToString(executionState), dest.Selector, offRamp.Address().String(), source.Selector, expectedSeqNr) return nil } case execEvent := <-sink: - t.Logf("Received ExecutionStateChanged for seqNum %d on chain %d (offramp %s) from chain %d", - execEvent.SequenceNumber, dest.Selector, offRamp.Address().String(), source.Selector) + t.Logf("Received ExecutionStateChanged (state %s) for seqNum %d on chain %d (offramp %s) from chain %d", + executionStateToString(execEvent.State), execEvent.SequenceNumber, dest.Selector, offRamp.Address().String(), source.Selector) if execEvent.SequenceNumber == expectedSeqNr && execEvent.SourceChainSelector == source.Selector { - t.Logf("Received ExecutionStateChanged on chain %d (offramp %s) from chain %d with expected sequence number %d", - dest.Selector, offRamp.Address().String(), source.Selector, expectedSeqNr) + t.Logf("Received ExecutionStateChanged (state %s) on chain %d (offramp %s) from chain %d with expected sequence number %d", + executionStateToString(execEvent.State), dest.Selector, offRamp.Address().String(), source.Selector, expectedSeqNr) return nil } case <-timer.C: diff --git a/deployment/ccip/test_helpers.go b/deployment/ccip/test_helpers.go index 74cf98cab9f..e6ad3987a0b 100644 --- a/deployment/ccip/test_helpers.go +++ b/deployment/ccip/test_helpers.go @@ -11,6 +11,7 @@ import ( mapset "github.com/deckarep/golang-set/v2" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" @@ -49,6 +50,11 @@ const ( FeedChainIndex = 1 ) +var ( + // bytes4 public constant EVM_EXTRA_ARGS_V2_TAG = 0x181dcf10; + evmExtraArgsV2Tag = hexutil.MustDecode("0x181dcf10") +) + // Context returns a context with the test's deadline, if available. func Context(tb testing.TB) context.Context { ctx := context.Background() @@ -199,6 +205,8 @@ func NewMemoryEnvironment(t *testing.T, lggr logger.Logger, numChains int, numNo } } +// NewMemoryEnvironmentWithJobs creates a new CCIP environment +// with capreg, fee tokens, feeds, nodes and jobs set up. func NewMemoryEnvironmentWithJobs(t *testing.T, lggr logger.Logger, numChains int, numNodes int) DeployedEnv { e := NewMemoryEnvironment(t, lggr, numChains, numNodes) e.SetupJobs(t) @@ -209,18 +217,15 @@ func CCIPSendRequest( e deployment.Environment, state CCIPOnChainState, src, dest uint64, - data []byte, - tokensAndAmounts []router.ClientEVMTokenAmount, - feeToken common.Address, testRouter bool, - extraArgs []byte, + evm2AnyMessage router.ClientEVM2AnyMessage, ) (*types.Transaction, uint64, error) { msg := router.ClientEVM2AnyMessage{ - Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32), - Data: data, - TokenAmounts: tokensAndAmounts, - FeeToken: feeToken, - ExtraArgs: extraArgs, + Receiver: evm2AnyMessage.Receiver, + Data: evm2AnyMessage.Data, + TokenAmounts: evm2AnyMessage.TokenAmounts, + FeeToken: evm2AnyMessage.FeeToken, + ExtraArgs: evm2AnyMessage.ExtraArgs, } r := state.Chains[src].Router if testRouter { @@ -249,10 +254,23 @@ func CCIPSendRequest( return tx, blockNum, nil } -func TestSendRequest(t *testing.T, e deployment.Environment, state CCIPOnChainState, src, dest uint64, testRouter bool, tokensAndAmounts []router.ClientEVMTokenAmount) uint64 { +func TestSendRequest( + t *testing.T, + e deployment.Environment, + state CCIPOnChainState, + src, dest uint64, + testRouter bool, + evm2AnyMessage router.ClientEVM2AnyMessage, +) (seqNum uint64) { t.Logf("Sending CCIP request from chain selector %d to chain selector %d", src, dest) - tx, blockNum, err := CCIPSendRequest(e, state, src, dest, []byte("hello"), tokensAndAmounts, common.HexToAddress("0x0"), testRouter, nil) + tx, blockNum, err := CCIPSendRequest( + e, + state, + src, dest, + testRouter, + evm2AnyMessage, + ) require.NoError(t, err) it, err := state.Chains[src].OnRamp.FilterCCIPMessageSent(&bind.FilterOpts{ Start: blockNum, @@ -261,11 +279,37 @@ func TestSendRequest(t *testing.T, e deployment.Environment, state CCIPOnChainSt }, []uint64{dest}, []uint64{}) require.NoError(t, err) require.True(t, it.Next()) - seqNum := it.Event.Message.Header.SequenceNumber - t.Logf("CCIP message sent from chain selector %d to chain selector %d tx %s seqNum %d", src, dest, tx.Hash().String(), seqNum) + seqNum = it.Event.Message.Header.SequenceNumber + nonce := it.Event.Message.Header.Nonce + sender := it.Event.Message.Sender + t.Logf("CCIP message sent from chain selector %d to chain selector %d tx %s seqNum %d nonce %d sender %s", + src, dest, tx.Hash().String(), seqNum, nonce, sender.String()) return seqNum } +func MakeExtraArgsV2(gasLimit uint64, allowOOO bool) []byte { + // extra args is the tag followed by the gas limit and allowOOO abi-encoded. + var extraArgs []byte + extraArgs = append(extraArgs, evmExtraArgsV2Tag...) + gasLimitBytes := new(big.Int).SetUint64(gasLimit).Bytes() + // pad from the left to 32 bytes + gasLimitBytes = common.LeftPadBytes(gasLimitBytes, 32) + + // abi-encode allowOOO + var allowOOOBytes []byte + if allowOOO { + allowOOOBytes = append(allowOOOBytes, 1) + } else { + allowOOOBytes = append(allowOOOBytes, 0) + } + // pad from the left to 32 bytes + allowOOOBytes = common.LeftPadBytes(allowOOOBytes, 32) + + extraArgs = append(extraArgs, gasLimitBytes...) + extraArgs = append(extraArgs, allowOOOBytes...) + return extraArgs +} + // AddLanesForAll adds densely connected lanes for all chains in the environment so that each chain // is connected to every other chain except itself. func AddLanesForAll(e deployment.Environment, state CCIPOnChainState) error { @@ -393,7 +437,13 @@ func ConfirmRequestOnSourceAndDest(t *testing.T, env deployment.Environment, sta require.NoError(t, err) startBlock := latesthdr.Number.Uint64() fmt.Printf("startblock %d", startBlock) - seqNum := TestSendRequest(t, env, state, sourceCS, destCS, false, nil) + seqNum := TestSendRequest(t, env, state, sourceCS, destCS, false, router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[destCS].Receiver.Address().Bytes(), 32), + Data: []byte("hello world"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }) require.Equal(t, expectedSeqNr, seqNum) fmt.Printf("Request sent for seqnr %d", seqNum) @@ -533,7 +583,7 @@ func setTokenPoolCounterPart( }, ) if err != nil { - return err + return fmt.Errorf("failed to apply chain updates on token pool %s: %w", tokenPool.Address(), err) } _, err = chain.Confirm(tx) @@ -546,6 +596,11 @@ func setTokenPoolCounterPart( destChainSelector, destTokenPoolAddress.Bytes(), ) + if err != nil { + return fmt.Errorf("failed to set remote pool on token pool %s: %w", tokenPool.Address(), err) + } + + _, err = chain.Confirm(tx) return err } diff --git a/deployment/go.mod b/deployment/go.mod index cde3a01968c..ce15ace764d 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -23,7 +23,7 @@ require ( github.com/sethvargo/go-retry v0.2.4 github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 github.com/smartcontractkit/chain-selectors v1.0.27 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4 github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 diff --git a/deployment/go.sum b/deployment/go.sum index 32d78868a01..e3a008b34e3 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1382,8 +1382,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 h1:BeLnOf2KKQpJj9nzfnE7QEg9ZqJ2jy/sbpNYVixVM2Y= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4 h1:e+uFsxQ21tMQKRu4oBXKycNzoR30vO/7STBtqtDvQJQ= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4/go.mod h1:ny87uTW6hLjCTLiBqBRNFEhETSXhHWevYlPclT5lSco= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= diff --git a/go.mod b/go.mod index ca0250d2917..a2242962894 100644 --- a/go.mod +++ b/go.mod @@ -76,7 +76,7 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chain-selectors v1.0.27 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4 github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241018134907-a00ba3729b5e diff --git a/go.sum b/go.sum index db766c87c1a..e20c421ceaa 100644 --- a/go.sum +++ b/go.sum @@ -1075,8 +1075,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 h1:BeLnOf2KKQpJj9nzfnE7QEg9ZqJ2jy/sbpNYVixVM2Y= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4 h1:e+uFsxQ21tMQKRu4oBXKycNzoR30vO/7STBtqtDvQJQ= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4/go.mod h1:ny87uTW6hLjCTLiBqBRNFEhETSXhHWevYlPclT5lSco= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 7bf5a0e1c36..18fba9e3031 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -36,7 +36,7 @@ require ( github.com/slack-go/slack v0.15.0 github.com/smartcontractkit/chain-selectors v1.0.27 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4 github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index a1f42b16aea..0a98d182126 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1403,8 +1403,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 h1:BeLnOf2KKQpJj9nzfnE7QEg9ZqJ2jy/sbpNYVixVM2Y= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4 h1:e+uFsxQ21tMQKRu4oBXKycNzoR30vO/7STBtqtDvQJQ= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4/go.mod h1:ny87uTW6hLjCTLiBqBRNFEhETSXhHWevYlPclT5lSco= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 441c6eaa4e1..2f25d98536d 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -64,7 +64,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f // indirect github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index a2c8cc52ba4..15cf08c7970 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1392,8 +1392,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 h1:BeLnOf2KKQpJj9nzfnE7QEg9ZqJ2jy/sbpNYVixVM2Y= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4 h1:e+uFsxQ21tMQKRu4oBXKycNzoR30vO/7STBtqtDvQJQ= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4/go.mod h1:ny87uTW6hLjCTLiBqBRNFEhETSXhHWevYlPclT5lSco= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= diff --git a/integration-tests/smoke/ccip_rmn_test.go b/integration-tests/smoke/ccip_rmn_test.go index c8d383f0122..d058b3c0e72 100644 --- a/integration-tests/smoke/ccip_rmn_test.go +++ b/integration-tests/smoke/ccip_rmn_test.go @@ -9,6 +9,7 @@ import ( mapset "github.com/deckarep/golang-set/v2" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" "github.com/stretchr/testify/require" @@ -19,6 +20,7 @@ import ( ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_remote" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testsetups" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -359,7 +361,13 @@ func runRmnTestCase(t *testing.T, tc rmnTestCase) { toChain := chainSelectors[msg.toChainIdx] for i := 0; i < msg.count; i++ { - seqNum := ccipdeployment.TestSendRequest(t, envWithRMN.Env, onChainState, fromChain, toChain, false, nil) + seqNum := ccipdeployment.TestSendRequest(t, envWithRMN.Env, onChainState, fromChain, toChain, false, router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(onChainState.Chains[toChain].Receiver.Address().Bytes(), 32), + Data: []byte("hello world"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }) expectedSeqNum[toChain] = seqNum t.Logf("Sent message from chain %d to chain %d with seqNum %d", fromChain, toChain, seqNum) } diff --git a/integration-tests/smoke/ccip_test.go b/integration-tests/smoke/ccip_test.go index 686f2c10299..5b0ba285527 100644 --- a/integration-tests/smoke/ccip_test.go +++ b/integration-tests/smoke/ccip_test.go @@ -4,6 +4,7 @@ import ( "math/big" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" @@ -83,7 +84,13 @@ func TestInitialDeployOnLocal(t *testing.T) { require.NoError(t, err) block := latesthdr.Number.Uint64() startBlocks[dest] = &block - seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, nil) + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32), + Data: []byte("hello world"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }) expectedSeqNum[dest] = seqNum } } @@ -223,11 +230,28 @@ func TestTokenTransfer(t *testing.T) { block := latesthdr.Number.Uint64() startBlocks[dest] = &block + var ( + receiver = common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32) + data = []byte("hello world") + feeToken = common.HexToAddress("0x0") + ) if src == tenv.HomeChainSel && dest == tenv.FeedChainSel { - seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, tokens[src]) + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, router.ClientEVM2AnyMessage{ + Receiver: receiver, + Data: data, + TokenAmounts: tokens[src], + FeeToken: feeToken, + ExtraArgs: nil, + }) expectedSeqNum[dest] = seqNum } else { - seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, nil) + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, router.ClientEVM2AnyMessage{ + Receiver: receiver, + Data: data, + TokenAmounts: nil, + FeeToken: feeToken, + ExtraArgs: nil, + }) expectedSeqNum[dest] = seqNum } }