From bb045250d163b0dd47d33e6ca35c252345b03da3 Mon Sep 17 00:00:00 2001 From: Makram Date: Wed, 13 Nov 2024 20:19:31 +0400 Subject: [PATCH] integration-tests/smoke: move ccip msging test (#15217) * integration-tests/smoke: move ccip msging test * fix imports * add messaging test to matrix * transform into docker based test --- .github/e2e-tests.yml | 16 +++- deployment/ccip/add_lane_test.go | 5 +- deployment/ccip/changeset/add_chain_test.go | 3 +- deployment/ccip/test_assertions.go | 55 ++++++++++-- deployment/ccip/test_helpers.go | 20 ++++- .../smoke/ccip_messaging_test.go | 84 +++++++++++++------ 6 files changed, 141 insertions(+), 42 deletions(-) rename deployment/ccip/changeset/messaging_test.go => integration-tests/smoke/ccip_messaging_test.go (71%) diff --git a/.github/e2e-tests.yml b/.github/e2e-tests.yml index 3b91bd251a1..aee250420e0 100644 --- a/.github/e2e-tests.yml +++ b/.github/e2e-tests.yml @@ -948,6 +948,20 @@ runner-test-matrix: test_env_vars: E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2 E2E_JD_VERSION: 0.4.0 + + - id: smoke/ccip_messaging_test.go:* + path: integration-tests/smoke/ccip_messaging_test.go + test_env_type: docker + runs_on: ubuntu-latest + triggers: + - PR E2E Core Tests + - Merge Queue E2E Core Tests + - Nightly E2E Tests + test_cmd: cd integration-tests/ && go test smoke/ccip_messaging_test.go -timeout 12m -test.parallel=1 -count=1 -json + pyroscope_env: ci-smoke-ccipv1_6-evm-simulated + test_env_vars: + E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2 + E2E_JD_VERSION: 0.4.0 # END: CCIPv1.6 tests @@ -1178,4 +1192,4 @@ runner-test-matrix: TEST_LOG_LEVEL: debug E2E_TEST_GRAFANA_DASHBOARD_URL: /d/6vjVx-1V8/ccip-long-running-tests - # END: CCIP tests \ No newline at end of file + # END: CCIP tests diff --git a/deployment/ccip/add_lane_test.go b/deployment/ccip/add_lane_test.go index 223d978b814..02fea79c911 100644 --- a/deployment/ccip/add_lane_test.go +++ b/deployment/ccip/add_lane_test.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" "github.com/smartcontractkit/chainlink/deployment" @@ -119,7 +120,7 @@ func TestAddLane(t *testing.T) { 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)) + require.NoError(t, commonutils.JustError(ConfirmExecWithSeqNr(t, e.Env.Chains[chain2], e.Env.Chains[chain1], state.Chains[chain1].OffRamp, &startBlock2, seqNum2))) // now check for the previous message from chain 1 to chain 2 that it has not been executed till now as the onRamp was disabled ConfirmNoExecConsistentlyWithSeqNr(t, e.Env.Chains[chain1], e.Env.Chains[chain2], state.Chains[chain2].OffRamp, seqNum1, 30*time.Second) @@ -145,5 +146,5 @@ func TestAddLane(t *testing.T) { ReplayLogs(t, e.Env.Offchain, replayBlocks) time.Sleep(30 * time.Second) // Now that the onRamp is enabled, the request should be processed - require.NoError(t, ConfirmExecWithSeqNr(t, e.Env.Chains[chain1], e.Env.Chains[chain2], state.Chains[chain2].OffRamp, &startBlock, seqNum1)) + require.NoError(t, commonutils.JustError(ConfirmExecWithSeqNr(t, e.Env.Chains[chain1], e.Env.Chains[chain2], state.Chains[chain2].OffRamp, &startBlock, seqNum1))) } diff --git a/deployment/ccip/changeset/add_chain_test.go b/deployment/ccip/changeset/add_chain_test.go index ff02430fd51..6a87bdd0a0a 100644 --- a/deployment/ccip/changeset/add_chain_test.go +++ b/deployment/ccip/changeset/add_chain_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/require" cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" + commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" @@ -224,7 +225,7 @@ func TestAddChainInbound(t *testing.T) { cciptypes.SeqNum(seqNr), })) require.NoError(t, - ccipdeployment.ConfirmExecWithSeqNr(t, e.Env.Chains[initialDeploy[0]], e.Env.Chains[newChain], state.Chains[newChain].OffRamp, &startBlock, seqNr)) + commonutils.JustError(ccipdeployment.ConfirmExecWithSeqNr(t, e.Env.Chains[initialDeploy[0]], e.Env.Chains[newChain], state.Chains[newChain].OffRamp, &startBlock, seqNr))) linkAddress := state.Chains[newChain].LinkToken.Address() feeQuoter := state.Chains[newChain].FeeQuoter diff --git a/deployment/ccip/test_assertions.go b/deployment/ccip/test_assertions.go index 373610531a1..64d1eb8571c 100644 --- a/deployment/ccip/test_assertions.go +++ b/deployment/ccip/test_assertions.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math/big" + "sync" "testing" "time" @@ -13,6 +14,7 @@ import ( "golang.org/x/sync/errgroup" "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/deployment" @@ -248,6 +250,26 @@ func ConfirmCommitWithExpectedSeqNumRange( } t.Logf("Waiting for commit report on chain selector %d from source selector %d expected seq nr range %s", dest.Selector, src.Selector, expectedSeqNumRange.String()) + + // Need to do this because the subscription sometimes fails to get the event. + iter, err := offRamp.FilterCommitReportAccepted(&bind.FilterOpts{ + Context: tests.Context(t), + }) + require.NoError(t, err) + for iter.Next() { + event := iter.Event + if len(event.MerkleRoots) > 0 { + for _, mr := range event.MerkleRoots { + if mr.SourceChainSelector == src.Selector && + uint64(expectedSeqNumRange.Start()) >= mr.MinSeqNr && + uint64(expectedSeqNumRange.End()) <= mr.MaxSeqNr { + t.Logf("Received commit report for [%d, %d] on selector %d from source selector %d expected seq nr range %s, token prices: %v", + mr.MinSeqNr, mr.MaxSeqNr, dest.Selector, src.Selector, expectedSeqNumRange.String(), event.PriceUpdates.TokenPriceUpdates) + return nil + } + } + } + } case subErr := <-subscription.Err(): return fmt.Errorf("subscription error: %w", subErr) case <-timer.C: @@ -272,6 +294,7 @@ func ConfirmCommitWithExpectedSeqNumRange( } // ConfirmExecWithSeqNrForAll waits for all chains in the environment to execute the given expectedSeqNums. +// If successful, it returns a map that maps the expected sequence numbers to their respective execution state. // 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. @@ -281,8 +304,12 @@ func ConfirmExecWithSeqNrForAll( state CCIPOnChainState, expectedSeqNums map[uint64]uint64, startBlocks map[uint64]*uint64, -) { - var wg errgroup.Group +) (executionStates map[uint64]int) { + var ( + wg errgroup.Group + mx sync.Mutex + ) + executionStates = make(map[uint64]int) for src, srcChain := range e.Chains { for dest, dstChain := range e.Chains { if src == dest { @@ -300,7 +327,7 @@ func ConfirmExecWithSeqNrForAll( return nil } - return ConfirmExecWithSeqNr( + executionState, err := ConfirmExecWithSeqNr( t, srcChain, dstChain, @@ -308,10 +335,20 @@ func ConfirmExecWithSeqNrForAll( startBlock, expectedSeqNums[dstChain.Selector], ) + if err != nil { + return err + } + + mx.Lock() + executionStates[expectedSeqNums[dstChain.Selector]] = executionState + mx.Unlock() + + return nil }) } } require.NoError(t, wg.Wait()) + return executionStates } // ConfirmExecWithSeqNr waits for an execution state change on the destination chain with the expected sequence number. @@ -323,7 +360,7 @@ func ConfirmExecWithSeqNr( offRamp *offramp.OffRamp, startBlock *uint64, expectedSeqNr uint64, -) error { +) (executionState int, err error) { timer := time.NewTimer(5 * time.Minute) defer timer.Stop() tick := time.NewTicker(5 * time.Second) @@ -334,7 +371,7 @@ func ConfirmExecWithSeqNr( Start: startBlock, }, sink, nil, nil, nil) if err != nil { - return fmt.Errorf("error to subscribe ExecutionStateChanged : %w", err) + return -1, fmt.Errorf("error to subscribe ExecutionStateChanged : %w", err) } defer subscription.Unsubscribe() for { @@ -346,7 +383,7 @@ func ConfirmExecWithSeqNr( 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 + return int(executionState), nil } case execEvent := <-sink: t.Logf("Received ExecutionStateChanged (state %s) for seqNum %d on chain %d (offramp %s) from chain %d", @@ -354,13 +391,13 @@ func ConfirmExecWithSeqNr( if execEvent.SequenceNumber == expectedSeqNr && execEvent.SourceChainSelector == source.Selector { 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 + return int(execEvent.State), nil } case <-timer.C: - return fmt.Errorf("timed out waiting for ExecutionStateChanged on chain %d (offramp %s) from chain %d with expected sequence number %d", + return -1, fmt.Errorf("timed out waiting for ExecutionStateChanged on chain %d (offramp %s) from chain %d with expected sequence number %d", dest.Selector, offRamp.Address().String(), source.Selector, expectedSeqNr) case subErr := <-subscription.Err(): - return fmt.Errorf("subscription error: %w", subErr) + return -1, fmt.Errorf("subscription error: %w", subErr) } } } diff --git a/deployment/ccip/test_helpers.go b/deployment/ccip/test_helpers.go index f62b8e15c79..cfa637ee9b0 100644 --- a/deployment/ccip/test_helpers.go +++ b/deployment/ccip/test_helpers.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" + commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" @@ -287,7 +288,9 @@ func TestSendRequest( return seqNum } -func MakeExtraArgsV2(gasLimit uint64, allowOOO bool) []byte { +// MakeEVMExtraArgsV2 creates the extra args for the EVM2Any message that is destined +// for an EVM chain. The extra args contain the gas limit and allow out of order flag. +func MakeEVMExtraArgsV2(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...) @@ -454,8 +457,19 @@ func ConfirmRequestOnSourceAndDest(t *testing.T, env deployment.Environment, sta })) fmt.Printf("Commit confirmed for seqnr %d", seqNum) - require.NoError(t, - ConfirmExecWithSeqNr(t, env.Chains[sourceCS], env.Chains[destCS], state.Chains[destCS].OffRamp, &startBlock, seqNum)) + require.NoError( + t, + commonutils.JustError( + ConfirmExecWithSeqNr( + t, + env.Chains[sourceCS], + env.Chains[destCS], + state.Chains[destCS].OffRamp, + &startBlock, + seqNum, + ), + ), + ) return nil } diff --git a/deployment/ccip/changeset/messaging_test.go b/integration-tests/smoke/ccip_messaging_test.go similarity index 71% rename from deployment/ccip/changeset/messaging_test.go rename to integration-tests/smoke/ccip_messaging_test.go index a5fde58742b..55309598c8c 100644 --- a/deployment/ccip/changeset/messaging_test.go +++ b/integration-tests/smoke/ccip_messaging_test.go @@ -1,4 +1,4 @@ -package changeset +package smoke import ( "testing" @@ -6,13 +6,18 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink/deployment" + ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testsetups" "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 { @@ -34,12 +39,13 @@ type messagingTestCaseOutput struct { nonce uint64 } -func Test_Messaging(t *testing.T) { - t.Parallel() - +func Test_CCIPMessaging(t *testing.T) { // Setup 2 chains and a single lane. - e := ccipdeployment.NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 2, 4) - state, err := ccipdeployment.LoadOnchainState(e.Env) + lggr := logger.TestLogger(t) + ctx := ccdeploy.Context(t) + e, _, _ := testsetups.NewLocalDevEnvironment(t, lggr) + + state, err := ccdeploy.LoadOnchainState(e.Env) require.NoError(t, err) allChainSelectors := maps.Keys(e.Env.Chains) @@ -54,20 +60,37 @@ func Test_Messaging(t *testing.T) { ) tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) - newAddresses := deployment.NewMemoryAddressBook() - err = ccipdeployment.DeployCCIPContracts(e.Env, newAddresses, ccipdeployment.DeployCCIPContractConfig{ + // Apply migration + output, err := changeset.InitialDeploy(e.Env, ccdeploy.DeployCCIPContractConfig{ HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, ChainsToDeploy: allChainSelectors, TokenConfig: tokenConfig, - MCMSConfig: ccipdeployment.NewTestMCMSConfig(t, e.Env), + MCMSConfig: ccdeploy.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, e.Env.ExistingAddresses.Merge(output.AddressBook)) + // Get new state after migration. + state, err = ccdeploy.LoadOnchainState(e.Env) require.NoError(t, err) + // Ensure capreg logs are up to date. + ccdeploy.ReplayLogs(t, e.Env.Offchain, e.ReplayBlocks) + + // Apply the jobs. + for nodeID, jobs := range output.JobSpecs { + for _, job := range jobs { + // Note these auto-accept + _, err := e.Env.Offchain.ProposeJob(ctx, + &jobv1.ProposeJobRequest{ + NodeId: nodeID, + Spec: job, + }) + require.NoError(t, err) + } + } + // connect a single lane, source to dest require.NoError(t, ccipdeployment.AddLane(e.Env, state, sourceChain, destChain)) @@ -94,6 +117,8 @@ func Test_Messaging(t *testing.T) { }, common.HexToAddress("0xdead"), []byte("hello eoa"), + nil, // default extraArgs + ccipdeployment.EXECUTION_STATE_SUCCESS, // success because offRamp won't call an EOA ) }) @@ -106,6 +131,8 @@ func Test_Messaging(t *testing.T) { }, state.Chains[destChain].FeeQuoter.Address(), []byte("hello FeeQuoter"), + nil, // default extraArgs + ccipdeployment.EXECUTION_STATE_SUCCESS, // success because offRamp won't call a contract not implementing CCIPReceiver ) }) @@ -118,6 +145,8 @@ func Test_Messaging(t *testing.T) { }, state.Chains[destChain].Receiver.Address(), []byte("hello CCIPReceiver"), + nil, // default extraArgs + ccipdeployment.EXECUTION_STATE_SUCCESS, func(t *testing.T) { iter, err := state.Chains[destChain].Receiver.FilterMessageReceived(nil) require.NoError(t, err) @@ -136,17 +165,8 @@ func Test_Messaging(t *testing.T) { }, 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") - }, + ccipdeployment.MakeEVMExtraArgsV2(1, false), // 1 gas is too low. + ccipdeployment.EXECUTION_STATE_FAILURE, // state would be failed onchain due to low gas ) }) } @@ -163,6 +183,8 @@ func runMessagingTestCase( tc messagingTestCase, receiver common.Address, msgData []byte, + extraArgs []byte, + expectedExecutionState int, extraAssertions ...func(t *testing.T), ) (out messagingTestCaseOutput) { // check latest nonce @@ -178,7 +200,7 @@ func runMessagingTestCase( Data: msgData, TokenAmounts: nil, FeeToken: common.HexToAddress("0x0"), - ExtraArgs: nil, + ExtraArgs: extraArgs, }) expectedSeqNum := make(map[uint64]uint64) expectedSeqNum[tc.destChain] = seqNum @@ -190,7 +212,17 @@ func runMessagingTestCase( } ccipdeployment.ConfirmCommitForAllWithExpectedSeqNums(tc.t, tc.deployedEnv.Env, tc.onchainState, expectedSeqNum, startBlocks) - ccipdeployment.ConfirmExecWithSeqNrForAll(tc.t, tc.deployedEnv.Env, tc.onchainState, expectedSeqNum, startBlocks) + execStates := ccipdeployment.ConfirmExecWithSeqNrForAll(tc.t, tc.deployedEnv.Env, tc.onchainState, expectedSeqNum, startBlocks) + + require.Equalf( + tc.t, + expectedExecutionState, + execStates[seqNum], + "wrong execution state for seq nr %d, expected %d, got %d", + seqNum, + expectedExecutionState, + execStates[seqNum], + ) // check the sender latestNonce on the dest, should be incremented latestNonce, err = tc.onchainState.Chains[tc.destChain].NonceManager.GetInboundNonce(&bind.CallOpts{