diff --git a/.github/e2e-tests.yml b/.github/e2e-tests.yml index f91166ebacb..3e23a7a4b41 100644 --- a/.github/e2e-tests.yml +++ b/.github/e2e-tests.yml @@ -935,14 +935,27 @@ runner-test-matrix: # START: CCIPv1.6 tests - - id: smoke/ccip/ccip_test.go:* + - id: smoke/ccip/ccip_test.go:^TestInitialDeployOnLocal$ path: integration-tests/smoke/ccip/ccip_test.go test_env_type: docker runs_on: ubuntu-latest triggers: - PR E2E Core Tests - Nightly E2E Tests - test_cmd: cd integration-tests/smoke/ccip && go test ccip_test.go -timeout 12m -test.parallel=2 -count=1 -json + test_cmd: cd integration-tests/smoke/ccip && go test -test.run ^TestInitialDeployOnLocal$ -timeout 12m -test.parallel=2 -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.6.0 + + - id: smoke/ccip/ccip_test.go:^TestTokenTransfer$ + path: integration-tests/smoke/ccip/ccip_test.go + test_env_type: docker + runs_on: ubuntu-latest + triggers: + - PR E2E Core Tests + - Nightly E2E Tests + test_cmd: cd integration-tests/smoke/ccip && go test -test.run ^TestTokenTransfer$ -timeout 12m -test.parallel=2 -count=1 -json pyroscope_env: ci-smoke-ccipv1_6-evm-simulated test_env_vars: E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2 @@ -981,7 +994,7 @@ runner-test-matrix: triggers: - PR E2E Core Tests - Nightly E2E Tests - test_cmd: cd integration-tests/smoke/ccip && go test ccip_usdc_test.go -timeout 18m -test.parallel=1 -count=1 -json + test_cmd: cd integration-tests/smoke/ccip && go test ccip_usdc_test.go -timeout 18m -test.parallel=5 -count=1 -json pyroscope_env: ci-smoke-ccipv1_6-evm-simulated test_env_vars: E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2,SIMULATED_3 diff --git a/deployment/ccip/changeset/deploy.go b/deployment/ccip/changeset/deploy.go index a53dd7f3f2c..63e6dab3350 100644 --- a/deployment/ccip/changeset/deploy.go +++ b/deployment/ccip/changeset/deploy.go @@ -315,7 +315,9 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } e.Logger.Infow("deployed ccip multicall", "addr", multicall3Contract.Address) } else { - e.Logger.Info("ccip multicall already deployed", "addr", mc3.Address) + if mc3 != nil { + e.Logger.Info("ccip multicall already deployed", "addr", mc3.Address) + } } if isUSDC { token, pool, messenger, transmitter, err1 := DeployUSDC(e.Logger, chain, ab, rmnProxy.Address(), r.Address()) diff --git a/deployment/ccip/changeset/deploy_home_chain.go b/deployment/ccip/changeset/deploy_home_chain.go index 881f7c386c3..2a65a2ca53e 100644 --- a/deployment/ccip/changeset/deploy_home_chain.go +++ b/deployment/ccip/changeset/deploy_home_chain.go @@ -18,8 +18,9 @@ import ( "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal" + + "github.com/smartcontractkit/chainlink/deployment" cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_home" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" @@ -478,7 +479,7 @@ func addDON( if _, err := deployment.ConfirmIfNoError(dest, tx, err); err != nil { return err } - + lggr.Infow("Set OCR3 Configs", "chainSelector", dest.Selector) mapOfframpOCR3Configs := make(map[cctypes.PluginType]offramp.MultiOCR3BaseOCRConfigArgs) for _, config := range offrampOCR3Configs { mapOfframpOCR3Configs[cctypes.PluginType(config.OcrPluginType)] = config diff --git a/deployment/ccip/changeset/test_helpers.go b/deployment/ccip/changeset/test_helpers.go index 5e8705d4757..12720d20f10 100644 --- a/deployment/ccip/changeset/test_helpers.go +++ b/deployment/ccip/changeset/test_helpers.go @@ -17,6 +17,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/chainlink-ccip/pluginconfig" + "golang.org/x/sync/errgroup" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" @@ -84,10 +85,11 @@ func Context(tb testing.TB) context.Context { } type DeployedEnv struct { - Env deployment.Environment - HomeChainSel uint64 - FeedChainSel uint64 - ReplayBlocks map[uint64]uint64 + Env deployment.Environment + HomeChainSel uint64 + FeedChainSel uint64 + ReplayBlocks map[uint64]uint64 + SenderAccounts map[uint64]*bind.TransactOpts } func (e *DeployedEnv) SetupJobs(t *testing.T) { @@ -708,40 +710,65 @@ func DeployTransferableToken( addresses deployment.AddressBook, token string, ) (*burn_mint_erc677.BurnMintERC677, *burn_mint_token_pool.BurnMintTokenPool, *burn_mint_erc677.BurnMintERC677, *burn_mint_token_pool.BurnMintTokenPool, error) { + deployGrp := errgroup.Group{} // Deploy token and pools - srcToken, srcPool, err := deployTransferTokenOneEnd(lggr, chains[src], addresses, token) - if err != nil { - return nil, nil, nil, nil, err - } - dstToken, dstPool, err := deployTransferTokenOneEnd(lggr, chains[dst], addresses, token) - if err != nil { - return nil, nil, nil, nil, err - } - - // Attach token pools to registry - if err := attachTokenToTheRegistry(chains[src], state.Chains[src], chains[src].DeployerKey, srcToken.Address(), srcPool.Address()); err != nil { - return nil, nil, nil, nil, err - } - - if err := attachTokenToTheRegistry(chains[dst], state.Chains[dst], chains[dst].DeployerKey, dstToken.Address(), dstPool.Address()); err != nil { - return nil, nil, nil, nil, err - } - - // Connect pool to each other - if err := setTokenPoolCounterPart(chains[src], srcPool, dst, dstToken.Address(), dstPool.Address()); err != nil { - return nil, nil, nil, nil, err - } + var srcToken *burn_mint_erc677.BurnMintERC677 + var srcPool *burn_mint_token_pool.BurnMintTokenPool + var dstToken *burn_mint_erc677.BurnMintERC677 + var dstPool *burn_mint_token_pool.BurnMintTokenPool + deployGrp.Go(func() error { + var err error + srcToken, srcPool, err = deployTransferTokenOneEnd(lggr, chains[src], addresses, token) + if err != nil { + return err + } + // Attach token pools to registry + if err := attachTokenToTheRegistry(chains[src], state.Chains[src], chains[src].DeployerKey, srcToken.Address(), srcPool.Address()); err != nil { + return err + } + return nil + }) + deployGrp.Go(func() error { + var err error + dstToken, dstPool, err = deployTransferTokenOneEnd(lggr, chains[dst], addresses, token) + if err != nil { + return err + } - if err := setTokenPoolCounterPart(chains[dst], dstPool, src, srcToken.Address(), srcPool.Address()); err != nil { + if err := attachTokenToTheRegistry(chains[dst], state.Chains[dst], chains[dst].DeployerKey, dstToken.Address(), dstPool.Address()); err != nil { + return err + } + return nil + }) + if err := deployGrp.Wait(); err != nil { return nil, nil, nil, nil, err } - - // Add burn/mint permissions - if err := grantMintBurnPermissions(lggr, chains[src], srcToken, srcPool.Address()); err != nil { - return nil, nil, nil, nil, err + if srcToken == nil || srcPool == nil || dstToken == nil || dstPool == nil { + return nil, nil, nil, nil, fmt.Errorf("failed to deploy token and pool") } - - if err := grantMintBurnPermissions(lggr, chains[dst], dstToken, dstPool.Address()); err != nil { + configurePoolGrp := errgroup.Group{} + configurePoolGrp.Go(func() error { + err := setTokenPoolCounterPart(chains[src], srcPool, dst, dstToken.Address(), dstPool.Address()) + if err != nil { + return fmt.Errorf("failed to set token pool counter part chain %d: %w", src, err) + } + err = grantMintBurnPermissions(lggr, chains[src], srcToken, srcPool.Address()) + if err != nil { + return fmt.Errorf("failed to grant mint burn permissions chain %d: %w", src, err) + } + return nil + }) + configurePoolGrp.Go(func() error { + err := setTokenPoolCounterPart(chains[dst], dstPool, src, srcToken.Address(), srcPool.Address()) + if err != nil { + return fmt.Errorf("failed to set token pool counter part chain %d: %w", dst, err) + } + if err := grantMintBurnPermissions(lggr, chains[dst], dstToken, dstPool.Address()); err != nil { + return fmt.Errorf("failed to grant mint burn permissions chain %d: %w", dst, err) + } + return nil + }) + if err := configurePoolGrp.Wait(); err != nil { return nil, nil, nil, nil, err } diff --git a/deployment/ccip/changeset/test_usdc_helpers.go b/deployment/ccip/changeset/test_usdc_helpers.go index 4a39f4e7ba1..6917f710988 100644 --- a/deployment/ccip/changeset/test_usdc_helpers.go +++ b/deployment/ccip/changeset/test_usdc_helpers.go @@ -1,9 +1,11 @@ package changeset import ( + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" "github.com/smartcontractkit/chainlink-ccip/pkg/reader" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -26,53 +28,53 @@ func ConfigureUSDCTokenPools( dstToken := state.Chains[dst].BurnMintTokens677[USDCSymbol] srcPool := state.Chains[src].USDCTokenPool dstPool := state.Chains[dst].USDCTokenPool - - // Attach token pools to registry - if err := attachTokenToTheRegistry(chains[src], state.Chains[src], chains[src].DeployerKey, srcToken.Address(), srcPool.Address()); err != nil { - lggr.Errorw("Failed to attach token to the registry", "err", err, "token", srcToken.Address(), "pool", srcPool.Address()) - return nil, nil, err - } - - if err := attachTokenToTheRegistry(chains[dst], state.Chains[dst], chains[dst].DeployerKey, dstToken.Address(), dstPool.Address()); err != nil { - lggr.Errorw("Failed to attach token to the registry", "err", err, "token", dstToken.Address(), "pool", dstPool.Address()) - return nil, nil, err - } - - // Connect pool to each other - if err := setUSDCTokenPoolCounterPart(chains[src], srcPool, dst, dstToken.Address(), dstPool.Address()); err != nil { - lggr.Errorw("Failed to set counter part", "err", err, "srcPool", srcPool.Address(), "dstPool", dstPool.Address()) - return nil, nil, err - } - - if err := setUSDCTokenPoolCounterPart(chains[dst], dstPool, src, srcToken.Address(), srcPool.Address()); err != nil { - lggr.Errorw("Failed to set counter part", "err", err, "srcPool", dstPool.Address(), "dstPool", srcPool.Address()) - return nil, nil, err - } - - // Add burn/mint permissions for source - for _, addr := range []common.Address{ - srcPool.Address(), - state.Chains[src].MockUSDCTokenMessenger.Address(), - state.Chains[src].MockUSDCTransmitter.Address(), - } { - if err := grantMintBurnPermissions(lggr, chains[src], srcToken, addr); err != nil { - lggr.Errorw("Failed to grant mint/burn permissions", "err", err, "token", srcToken.Address(), "minter", addr) - return nil, nil, err + configureGrp := errgroup.Group{} + configureGrp.Go(func() error { + if err := attachTokenToTheRegistry(chains[src], state.Chains[src], chains[src].DeployerKey, srcToken.Address(), srcPool.Address()); err != nil { + lggr.Errorw("Failed to attach token to the registry", "chain", src, "err", err, "token", srcToken.Address(), "pool", srcPool.Address()) + return err } - } - - // Add burn/mint permissions for dest - for _, addr := range []common.Address{ - dstPool.Address(), - state.Chains[dst].MockUSDCTokenMessenger.Address(), - state.Chains[dst].MockUSDCTransmitter.Address(), - } { - if err := grantMintBurnPermissions(lggr, chains[dst], dstToken, addr); err != nil { - lggr.Errorw("Failed to grant mint/burn permissions", "err", err, "token", dstToken.Address(), "minter", addr) - return nil, nil, err + // Connect pool to each other + if err := setUSDCTokenPoolCounterPart(chains[src], srcPool, dst, dstToken.Address(), dstPool.Address()); err != nil { + lggr.Errorw("Failed to set counter part", "err", err, "srcPool", srcPool.Address(), "dstPool", dstPool.Address()) + return err + } + for _, addr := range []common.Address{ + srcPool.Address(), + state.Chains[src].MockUSDCTokenMessenger.Address(), + state.Chains[src].MockUSDCTransmitter.Address(), + } { + if err := grantMintBurnPermissions(lggr, chains[src], srcToken, addr); err != nil { + lggr.Errorw("Failed to grant mint/burn permissions", "err", err, "token", srcToken.Address(), "minter", addr) + return err + } + } + return nil + }) + configureGrp.Go(func() error { + if err := attachTokenToTheRegistry(chains[dst], state.Chains[dst], chains[dst].DeployerKey, dstToken.Address(), dstPool.Address()); err != nil { + lggr.Errorw("Failed to attach token to the registry", "chain", dst, "err", err, "token", dstToken.Address(), "pool", dstPool.Address()) + return err } + if err := setUSDCTokenPoolCounterPart(chains[dst], dstPool, src, srcToken.Address(), srcPool.Address()); err != nil { + lggr.Errorw("Failed to set counter part", "err", err, "srcPool", dstPool.Address(), "dstPool", srcPool.Address()) + return err + } + for _, addr := range []common.Address{ + dstPool.Address(), + state.Chains[dst].MockUSDCTokenMessenger.Address(), + state.Chains[dst].MockUSDCTransmitter.Address(), + } { + if err := grantMintBurnPermissions(lggr, chains[dst], dstToken, addr); err != nil { + lggr.Errorw("Failed to grant mint/burn permissions", "err", err, "token", dstToken.Address(), "minter", addr) + return err + } + } + return nil + }) + if err := configureGrp.Wait(); err != nil { + return nil, nil, fmt.Errorf("failed to configure USDC token pools: %w", err) } - return srcToken, dstToken, nil } diff --git a/deployment/common/changeset/internal/mcms.go b/deployment/common/changeset/internal/mcms.go index 1e2fb958aae..dfa30a1dcac 100644 --- a/deployment/common/changeset/internal/mcms.go +++ b/deployment/common/changeset/internal/mcms.go @@ -4,8 +4,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" + "golang.org/x/sync/errgroup" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" @@ -62,13 +64,19 @@ func DeployMCMSWithTimelockContractsBatch( ab deployment.AddressBook, cfgByChain map[uint64]types.MCMSWithTimelockConfig, ) error { + deployGrp := errgroup.Group{} for chainSel, cfg := range cfgByChain { - _, err := DeployMCMSWithTimelockContracts(lggr, chains[chainSel], ab, cfg) - if err != nil { - return err - } + cfg := cfg + chainSel := chainSel + deployGrp.Go(func() error { + _, err := DeployMCMSWithTimelockContracts(lggr, chains[chainSel], ab, cfg) + if err != nil { + return err + } + return nil + }) } - return nil + return deployGrp.Wait() } // DeployMCMSWithTimelockContracts deploys an MCMS for @@ -126,6 +134,7 @@ func DeployMCMSWithTimelockContracts( lggr.Errorw("Failed to grant timelock admin role", "err", err) return nil, err } + lggr.Infow("granted timelock admin role", "addr", timelock.Address) // After the proposer cycle is validated, // we can remove the deployer as an admin. return &MCMSWithTimelockDeploy{ diff --git a/integration-tests/smoke/ccip/ccip_usdc_test.go b/integration-tests/smoke/ccip/ccip_usdc_test.go index f2c2a45df86..18969543ae9 100644 --- a/integration-tests/smoke/ccip/ccip_usdc_test.go +++ b/integration-tests/smoke/ccip/ccip_usdc_test.go @@ -8,8 +8,7 @@ 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" + "golang.org/x/sync/errgroup" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" @@ -44,7 +43,8 @@ func TestUSDCTokenTransfer(t *testing.T) { state, err := changeset.LoadOnchainState(e) require.NoError(t, err) - allChainSelectors := maps.Keys(e.Chains) + allChainSelectors := e.AllChainSelectors() + require.Len(t, allChainSelectors, 3, "expected 3 chains for this test") chainA := allChainSelectors[0] chainC := allChainSelectors[1] chainB := allChainSelectors[2] @@ -68,26 +68,29 @@ func TestUSDCTokenTransfer(t *testing.T) { // Add all lanes require.NoError(t, changeset.AddLanesForAll(e, state)) - + changeset.ReplayLogs(t, e.Offchain, tenv.ReplayBlocks) mintAndAllow(t, e, state, map[uint64][]*burn_mint_erc677.BurnMintERC677{ chainA: {aChainUSDC, aChainToken}, chainB: {bChainUSDC}, chainC: {cChainUSDC, cChainToken}, }) + updateFeeQtrGrp := errgroup.Group{} + updateFeeQtrGrp.Go(func() error { + return changeset.UpdateFeeQuoterForUSDC(lggr, e.Chains[chainA], state.Chains[chainA], chainC, aChainUSDC) + }) - err = changeset.UpdateFeeQuoterForUSDC(lggr, e.Chains[chainA], state.Chains[chainA], chainC, aChainUSDC) - require.NoError(t, err) - - err = changeset.UpdateFeeQuoterForUSDC(lggr, e.Chains[chainB], state.Chains[chainB], chainC, bChainUSDC) - require.NoError(t, err) - - err = changeset.UpdateFeeQuoterForUSDC(lggr, e.Chains[chainC], state.Chains[chainC], chainA, cChainUSDC) - require.NoError(t, err) + updateFeeQtrGrp.Go(func() error { + return changeset.UpdateFeeQuoterForUSDC(lggr, e.Chains[chainB], state.Chains[chainB], chainC, bChainUSDC) + }) + updateFeeQtrGrp.Go(func() error { + return changeset.UpdateFeeQuoterForUSDC(lggr, e.Chains[chainC], state.Chains[chainC], chainA, cChainUSDC) + }) + require.NoError(t, updateFeeQtrGrp.Wait()) // MockE2EUSDCTransmitter always mint 1, see MockE2EUSDCTransmitter.sol for more details tinyOneCoin := new(big.Int).SetUint64(1) - tcs := []struct { + id int name string receiver common.Address sourceChain uint64 @@ -96,6 +99,8 @@ func TestUSDCTokenTransfer(t *testing.T) { data []byte expectedTokenBalances map[common.Address]*big.Int expectedExecutionState int + transferReturnData transferReturnData + initialBalances map[common.Address]*big.Int }{ { name: "single USDC token transfer to EOA", @@ -173,28 +178,41 @@ func TestUSDCTokenTransfer(t *testing.T) { }, } + for i, tt := range tcs { + tcs[i].initialBalances = make(map[common.Address]*big.Int) + for token := range tt.expectedTokenBalances { + initialBalance := getTokenBalance(t, token, tt.receiver, e.Chains[tt.destChain]) + tcs[i].initialBalances[token] = initialBalance + } + + // Send all requests first and update the return data + tcs[i].transferReturnData = transfer(t, e, state, tt.sourceChain, tt.destChain, router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(tt.receiver.Bytes(), 32), + Data: tt.data, + TokenAmounts: tt.tokens, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }) + } + for _, tt := range tcs { + tt := tt t.Run(tt.name, func(t *testing.T) { - initialBalances := map[common.Address]*big.Int{} - for token := range tt.expectedTokenBalances { - initialBalance := getTokenBalance(t, token, tt.receiver, e.Chains[tt.destChain]) - initialBalances[token] = initialBalance - } + t.Parallel() - transferAndWaitForSuccess( + waitForSuccess( t, e, state, tt.sourceChain, tt.destChain, - tt.tokens, - tt.receiver, - tt.data, + tt.transferReturnData, tt.expectedExecutionState, ) for token, balance := range tt.expectedTokenBalances { - expected := new(big.Int).Add(initialBalances[token], balance) + t.Log("Checking token balance for token", token, "receiver", tt.receiver, "dest chain", tt.destChain) + expected := new(big.Int).Add(tt.initialBalances[token], balance) waitForTheTokenBalance(t, token, tt.receiver, e.Chains[tt.destChain], expected) } }) @@ -275,17 +293,19 @@ func mintAndAllow( } } -// transferAndWaitForSuccess sends a message from sourceChain to destChain and waits for it to be executed -func transferAndWaitForSuccess( +type transferReturnData struct { + startBlocks map[uint64]*uint64 + expectedSeqNum map[changeset.SourceDestPair]uint64 + expectedSeqNumExec map[changeset.SourceDestPair][]uint64 +} + +func transfer( t *testing.T, env deployment.Environment, state changeset.CCIPOnChainState, sourceChain, destChain uint64, - tokens []router.ClientEVMTokenAmount, - receiver common.Address, - data []byte, - expectedStatus int, -) { + evm2AnyMessage router.ClientEVM2AnyMessage, +) transferReturnData { identifier := changeset.SourceDestPair{ SourceChainSelector: sourceChain, DestChainSelector: destChain, @@ -300,22 +320,40 @@ func transferAndWaitForSuccess( block := latesthdr.Number.Uint64() startBlocks[destChain] = &block - msgSentEvent := changeset.TestSendRequest(t, env, state, sourceChain, destChain, false, router.ClientEVM2AnyMessage{ - Receiver: common.LeftPadBytes(receiver.Bytes(), 32), - Data: data, - TokenAmounts: tokens, - FeeToken: common.HexToAddress("0x0"), - ExtraArgs: nil, - }) + msgSentEvent := changeset.TestSendRequest(t, env, state, sourceChain, destChain, false, evm2AnyMessage) expectedSeqNum[identifier] = msgSentEvent.SequenceNumber expectedSeqNumExec[identifier] = []uint64{msgSentEvent.SequenceNumber} + return transferReturnData{ + startBlocks: startBlocks, + expectedSeqNum: expectedSeqNum, + expectedSeqNumExec: expectedSeqNumExec, + } +} + +// transferAndWaitForSuccess sends a message from sourceChain to destChain and waits for it to be executed +func waitForSuccess( + t *testing.T, + env deployment.Environment, + state changeset.CCIPOnChainState, + sourceChain, destChain uint64, + transferData transferReturnData, + expectedStatus int, +) { + identifier := changeset.SourceDestPair{ + SourceChainSelector: sourceChain, + DestChainSelector: destChain, + } + + startBlocks := transferData.startBlocks + expectedSeqNum := transferData.expectedSeqNum + expectedSeqNumExec := transferData.expectedSeqNumExec // Wait for all commit reports to land. changeset.ConfirmCommitForAllWithExpectedSeqNums(t, env, state, expectedSeqNum, startBlocks) // Wait for all exec reports to land states := changeset.ConfirmExecWithSeqNrsForAll(t, env, state, expectedSeqNumExec, startBlocks) - require.Equal(t, expectedStatus, states[identifier][msgSentEvent.SequenceNumber]) + require.Equal(t, expectedStatus, states[identifier][expectedSeqNum[identifier]]) } func waitForTheTokenBalance( diff --git a/integration-tests/testconfig/ccip/ccip.toml b/integration-tests/testconfig/ccip/ccip.toml index 85e645ed0b9..579aeef326e 100644 --- a/integration-tests/testconfig/ccip/ccip.toml +++ b/integration-tests/testconfig/ccip/ccip.toml @@ -3,7 +3,7 @@ chainlink_node_funding = 1 [Network] -selected_networks = ['SIMULATED_1', 'SIMULATED_2'] +selected_networks = ['SIMULATED_1', 'SIMULATED_2', 'SIMULATED_3'] [Network.EVMNetworks.SIMULATED_1] evm_name = 'chain-1337' diff --git a/integration-tests/testsetups/test_helpers.go b/integration-tests/testsetups/test_helpers.go index f33dacb3851..bad2e4d3ccd 100644 --- a/integration-tests/testsetups/test_helpers.go +++ b/integration-tests/testsetups/test_helpers.go @@ -56,6 +56,10 @@ import ( "google.golang.org/grpc/credentials/insecure" ) +const ( + PARALLEL_FUNDING_ENV_VAR = "PARALLEL_FUNDING" +) + // DeployedLocalDevEnvironment is a helper struct for setting up a local dev environment with docker type DeployedLocalDevEnvironment struct { changeset.DeployedEnv @@ -130,7 +134,20 @@ func NewLocalDevEnvironment( // fund the nodes zeroLogLggr := logging.GetTestLogger(t) - FundNodes(t, zeroLogLggr, testEnv, cfg, don.PluginNodes()) + // check if we can fund in parallel + var parallelFunding bool + if val := os.Getenv(PARALLEL_FUNDING_ENV_VAR); val != "" { + parallelFunding, err = strconv.ParseBool(val) + require.NoError(t, err) + } + fundGrp := errgroup.Group{} + if parallelFunding { + fundGrp.Go(func() error { + return FundNodes(t, zeroLogLggr, testEnv, cfg, don.PluginNodes()) + }) + } else { + require.NoError(t, FundNodes(t, zeroLogLggr, testEnv, cfg, don.PluginNodes())) + } env := *e envNodes, err := deployment.NodeInfo(env.NodeIDs, env.Offchain) @@ -247,6 +264,7 @@ func NewLocalDevEnvironment( // Ensure capreg logs are up to date. changeset.ReplayLogs(t, e.Offchain, replayBlocks) + require.NoError(t, fundGrp.Wait()) return changeset.DeployedEnv{ Env: env, @@ -437,6 +455,12 @@ func CreateDockerEnv(t *testing.T) ( evmNetworks[i].URLs = rpcProvider.PrivateWsUrsl() } env.EVMNetworks = append(env.EVMNetworks, &evmNetworks[i]) + // if number of private keys is more than 1, we can fund the nodes in parallel + if len(evmNetworks[i].PrivateKeys) > 1 { + require.NoError(t, os.Setenv(PARALLEL_FUNDING_ENV_VAR, "true")) + } else { + require.NoError(t, os.Setenv(PARALLEL_FUNDING_ENV_VAR, "false")) + } } chains := CreateChainConfigFromNetworks(t, env, privateEthereumNetworks, cfg.GetNetworkConfig()) @@ -559,13 +583,15 @@ func StartChainlinkNodes( // FundNodes sends funds to the chainlink nodes based on the provided test config // It also sets up a clean-up function to return the funds back to the deployer account once the test is done // It assumes that the chainlink nodes are already started and the account addresses for all chains are available -func FundNodes(t *testing.T, lggr zerolog.Logger, env *test_env.CLClusterTestEnv, cfg tc.TestConfig, nodes []devenv.Node) { +func FundNodes(t *testing.T, lggr zerolog.Logger, env *test_env.CLClusterTestEnv, cfg tc.TestConfig, nodes []devenv.Node) error { evmNetworks := networks.MustGetSelectedNetworkConfig(cfg.GetNetworkConfig()) for i, net := range evmNetworks { // if network is simulated, update the URLs with deployed chain RPCs in the docker test environment if net.Simulated { rpcProvider, err := env.GetRpcProvider(net.ChainID) - require.NoError(t, err, "Error getting rpc provider") + if err != nil { + return fmt.Errorf("error getting rpc provider: %w", err) + } evmNetworks[i].HTTPURLs = rpcProvider.PublicHttpUrls() evmNetworks[i].URLs = rpcProvider.PublicWsUrls() } @@ -577,6 +603,7 @@ func FundNodes(t *testing.T, lggr zerolog.Logger, env *test_env.CLClusterTestEnv continue } evmNetwork := evmNetworks[i] + evmNetwork.PrivateKeys = []string{evmNetwork.PrivateKeys[len(evmNetwork.PrivateKeys)-1]} sethClient, err := utils.TestAwareSethClient(t, cfg, &evmNetwork) require.NoError(t, err, "Error getting seth client for network %s", evmNetwork.Name) require.Greater(t, len(sethClient.PrivateKeys), 0, seth.ErrNoKeyLoaded) @@ -594,8 +621,14 @@ func FundNodes(t *testing.T, lggr zerolog.Logger, env *test_env.CLClusterTestEnv }) fundGrp := errgroup.Group{} for i := range evmNetworks { + i := i fundGrp.Go(func() error { evmNetwork := evmNetworks[i] + // use the last private key to fund the nodes + // generally the first key is used as deployer key + // this way we can fund the nodes and deploy contracts in parallel without running into + // nonce issues + evmNetwork.PrivateKeys = []string{evmNetwork.PrivateKeys[len(evmNetwork.PrivateKeys)-1]} sethClient, err := utils.TestAwareSethClient(t, cfg, &evmNetwork) if err != nil { return fmt.Errorf("error getting seth client for network %s: %w", evmNetwork.Name, err) @@ -643,7 +676,10 @@ func FundNodes(t *testing.T, lggr zerolog.Logger, env *test_env.CLClusterTestEnv return nil }) } - require.NoError(t, fundGrp.Wait(), "Error funding chainlink nodes") + if err := fundGrp.Wait(); err != nil { + return fmt.Errorf("error funding chainlink nodes: %w", err) + } + return nil } // CreateChainConfigFromNetworks creates a list of ChainConfig from the network config provided in test config.