From 600bc33fd66e76c613292a42cd08b4a1271b15fd Mon Sep 17 00:00:00 2001 From: Austin <107539019+0xAustinWang@users.noreply.github.com> Date: Fri, 25 Oct 2024 00:03:30 +1100 Subject: [PATCH] Active / Candidate test (#14838) * add mcms batch accumulator for running integ tests * add functions for proposing and promoting candidates in MCMS * add some more execution for mcms, breaking at timelock reverting on underlying transaction * format calls * more fixes * fixes * clean up * fix lint * review comments * review comments * fix lint * review comments * fix tests * more review comments * add sens requests * review comments * send a dummy req * add initial test for bluegreen migration * add ability to update number of nodes * working test, remove a node from candidate configuration, see working transmit * add active/candidate test without offchain confirmation * remove integration test binary * move code around * check for 0x0000 instead of nil from post-promotion candidate config * partial bluegreen * refactor to make plugin types generic * deploy job specs before transmitting message, pass tests * fix golang lint ci issues * passing changeset test * working test, no mcms * wip * passing test with candidate in mcms * passing test, skipping send txn after setting candidate * fix bug in transition code for active candidate * update changes that were made to deployment and launcher.go * get test passing, commit * update for linting in launcher.go * clean up, move transferownership into ownership file * lint * fixes from comments in the pr * go mod tidy * fix address type * moving imports around * fix issues from merging * fix goimport * fix go imports --------- Co-authored-by: AnieeG --- .gitignore | 1 + core/capabilities/ccip/launcher/deployment.go | 15 +- core/capabilities/ccip/launcher/launcher.go | 26 +- .../deployment/ccip/add_chain.go | 60 ++--- .../deployment/ccip/add_chain_test.go | 8 +- .../deployment/ccip/add_lane_test.go | 2 +- .../active_candidate_changeset_test.go | 253 ++++++++++++++++++ .../ccip/changeset/initial_deploy_test.go | 2 +- .../deployment/ccip/deploy_home_chain.go | 105 +++----- .../deployment/ccip/ownership.go | 37 +++ integration-tests/deployment/ccip/propose.go | 70 ++--- .../deployment/ccip/propose_home_chain.go | 38 +-- .../deployment/ccip/test_helpers.go | 31 ++- integration-tests/go.mod | 2 +- 14 files changed, 461 insertions(+), 189 deletions(-) create mode 100644 integration-tests/deployment/ccip/changeset/active_candidate_changeset_test.go create mode 100644 integration-tests/deployment/ccip/ownership.go diff --git a/.gitignore b/.gitignore index 718d4e4fe82..34d25ebb472 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,7 @@ __debug_bin* db_dumps/ .run.id integration-tests/**/traces/ +integration-tests/**/integration-tests benchmark_report.csv benchmark_summary.json secrets.toml diff --git a/core/capabilities/ccip/launcher/deployment.go b/core/capabilities/ccip/launcher/deployment.go index bed6296549e..631c02d5cc9 100644 --- a/core/capabilities/ccip/launcher/deployment.go +++ b/core/capabilities/ccip/launcher/deployment.go @@ -31,10 +31,19 @@ type ccipDeployment struct { // Close shuts down all OCR instances in the deployment. func (c *ccipDeployment) Close() error { + // we potentially run into this situation when + // trying to close an active instance that doesn't exist + // this check protects us from nil pointer exception + + if c == nil { + return nil + } var err error // shutdown active commit instance. - err = multierr.Append(err, c.commit.active.Close()) + if c.commit.active != nil { + err = multierr.Append(err, c.commit.active.Close()) + } // shutdown candidate commit instance. if c.commit.candidate != nil { @@ -42,7 +51,9 @@ func (c *ccipDeployment) Close() error { } // shutdown active exec instance. - err = multierr.Append(err, c.exec.active.Close()) + if c.exec.active != nil { + err = multierr.Append(err, c.exec.active.Close()) + } // shutdown candidate exec instance. if c.exec.candidate != nil { diff --git a/core/capabilities/ccip/launcher/launcher.go b/core/capabilities/ccip/launcher/launcher.go index bc351291dc0..6b512d142c1 100644 --- a/core/capabilities/ccip/launcher/launcher.go +++ b/core/capabilities/ccip/launcher/launcher.go @@ -2,6 +2,7 @@ package launcher import ( "context" + "errors" "fmt" "sync" "time" @@ -189,6 +190,10 @@ func (l *launcher) processUpdate(updated map[registrysyncer.DonID]registrysyncer if !ok { return fmt.Errorf("invariant violation: expected to find CCIP DON %d in the map of running deployments", don.ID) } + // we encounter this when a node is removed from the don + if prevDeployment == nil { + return errors.New("this node was closed") + } futDeployment, err := updateDON( l.lggr, @@ -201,16 +206,19 @@ func (l *launcher) processUpdate(updated map[registrysyncer.DonID]registrysyncer if err != nil { return err } - if err := futDeployment.TransitionDeployment(prevDeployment); err != nil { - // TODO: how to handle a failed active-candidate deployment? - return fmt.Errorf("failed to handle active-candidate deployment for CCIP DON %d: %w", donID, err) - } + // When we remove a node from the don, this node does not have a future deployment + if futDeployment != nil { + if err := futDeployment.TransitionDeployment(prevDeployment); err != nil { + // TODO: how to handle a failed active-candidate deployment? + return fmt.Errorf("failed to handle active-candidate deployment for CCIP DON %d: %w", donID, err) + } - // update state. - l.dons[donID] = futDeployment - // update the state with the latest config. - // this way if one of the starts errors, we don't retry all of them. - l.regState.IDsToDONs[donID] = updated[donID] + // update state. + l.dons[donID] = futDeployment + // update the state with the latest config. + // this way if one of the starts errors, we don't retry all of them. + l.regState.IDsToDONs[donID] = updated[donID] + } } return nil diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index bc6ff67ba1c..3c308e8d977 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -4,13 +4,12 @@ import ( "fmt" "math/big" - "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" "github.com/smartcontractkit/chainlink/integration-tests/deployment" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" ) @@ -26,7 +25,6 @@ func NewChainInboundProposal( ) (*timelock.MCMSWithTimelockProposal, error) { // Generate proposal which enables new destination (from test router) on all source chains. var batches []timelock.BatchChainOperation - var chains []uint64 for _, source := range sources { enableOnRampDest, err := state.Chains[source].OnRamp.ApplyDestChainConfigUpdates(deployment.SimTransactOpts(), []onramp.OnRampDestChainConfigArgs{ { @@ -64,47 +62,33 @@ func NewChainInboundProposal( }, }, }) - chains = append(chains, source) } addChainOp, err := ApplyChainConfigUpdatesOp(e, state, homeChainSel, []uint64{newChainSel}) if err != nil { return nil, err } - timelockAddresses, metaDataPerChain, err := BuildProposalMetadata(state, append(chains, homeChainSel)) - if err != nil { - return nil, err - } + batches = append(batches, timelock.BatchChainOperation{ ChainIdentifier: mcms.ChainIdentifier(homeChainSel), Batch: []mcms.Operation{ addChainOp, }, }) - return timelock.NewMCMSWithTimelockProposal( - "1", - 2004259681, // TODO: should be parameterized and based on current block timestamp. - []mcms.Signature{}, - false, - metaDataPerChain, - timelockAddresses, - "blah", // TODO - batches, - timelock.Schedule, - "0s", // TODO: Should be parameterized. - ) + + return BuildProposalFromBatches(state, batches, "proposal to set new chains", 0) } -// AddDonAndSetCandidateForCommitProposal adds new DON for destination to home chain +// AddDonAndSetCandidateProposal adds new DON for destination to home chain // and sets the commit plugin config as candidateConfig for the don. -func AddDonAndSetCandidateForCommitProposal( +func AddDonAndSetCandidateProposal( state CCIPOnChainState, e deployment.Environment, nodes deployment.Nodes, ocrSecrets deployment.OCRSecrets, homeChainSel, feedChainSel, newChainSel uint64, tokenConfig TokenConfig, - rmnHomeAddress common.Address, + pluginType types.PluginType, ) (*timelock.MCMSWithTimelockProposal, error) { newDONArgs, err := BuildOCR3ConfigForCCIPHome( e.Logger, @@ -114,7 +98,7 @@ func AddDonAndSetCandidateForCommitProposal( feedChainSel, tokenConfig.GetTokenInfo(e.Logger, state.Chains[newChainSel].LinkToken, state.Chains[newChainSel].Weth9), nodes.NonBootstraps(), - rmnHomeAddress, + state.Chains[homeChainSel].RMNHome.Address(), ) if err != nil { return nil, err @@ -123,12 +107,12 @@ func AddDonAndSetCandidateForCommitProposal( if err != nil { return nil, err } - commitConfig, ok := newDONArgs[types.PluginTypeCCIPCommit] + commitConfig, ok := newDONArgs[pluginType] if !ok { return nil, fmt.Errorf("missing commit plugin in ocr3Configs") } donID := latestDon.Id + 1 - addDonOp, err := SetCandidateCommitPluginWithAddDonOps( + addDonOp, err := NewDonWithCandidateOp( donID, commitConfig, state.Chains[homeChainSel].CapabilityRegistry, nodes.NonBootstraps(), @@ -136,23 +120,9 @@ func AddDonAndSetCandidateForCommitProposal( if err != nil { return nil, err } - timelockAddresses, metaDataPerChain, err := BuildProposalMetadata(state, []uint64{homeChainSel}) - if err != nil { - return nil, err - } - return timelock.NewMCMSWithTimelockProposal( - "1", - 2004259681, // TODO: should be parameterized and based on current block timestamp. - []mcms.Signature{}, - false, - metaDataPerChain, - timelockAddresses, - "SetCandidate for commit And AddDon for new chain", - []timelock.BatchChainOperation{{ - ChainIdentifier: mcms.ChainIdentifier(homeChainSel), - Batch: []mcms.Operation{addDonOp}, - }}, - timelock.Schedule, - "0s", // TODO: Should be parameterized. - ) + + return BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ + ChainIdentifier: mcms.ChainIdentifier(homeChainSel), + Batch: []mcms.Operation{addDonOp}, + }}, "setCandidate for commit and AddDon on new Chain", 0) } diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index 025e8aeaa45..94411e06e57 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" @@ -25,7 +27,7 @@ import ( func TestAddChainInbound(t *testing.T) { // 4 chains where the 4th is added after initial deployment. - e := NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 4) + e := NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 4, 4) state, err := LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) // Take first non-home chain as the new chain. @@ -148,14 +150,14 @@ func TestAddChainInbound(t *testing.T) { //SendRequest(t, e.Env, state, initialDeploy[0], newChain, true) t.Logf("Executing add don and set candidate proposal for commit plugin on chain %d", newChain) - addDonProp, err := AddDonAndSetCandidateForCommitProposal(state, e.Env, nodes, deployment.XXXGenerateTestOCRSecrets(), e.HomeChainSel, e.FeedChainSel, newChain, tokenConfig, common.HexToAddress(rmnHomeAddress)) + addDonProp, err := AddDonAndSetCandidateProposal(state, e.Env, nodes, deployment.XXXGenerateTestOCRSecrets(), e.HomeChainSel, e.FeedChainSel, newChain, tokenConfig, types.PluginTypeCCIPCommit) require.NoError(t, err) addDonExec := SignProposal(t, e.Env, addDonProp) ExecuteProposal(t, e.Env, addDonExec, state, e.HomeChainSel) t.Logf("Executing promote candidate proposal for exec plugin on chain %d", newChain) - setCandidateForExecProposal, err := SetCandidateExecPluginProposal(state, e.Env, nodes, deployment.XXXGenerateTestOCRSecrets(), e.HomeChainSel, e.FeedChainSel, newChain, tokenConfig, common.HexToAddress(rmnHomeAddress)) + setCandidateForExecProposal, err := SetCandidatePluginProposal(state, e.Env, nodes, deployment.XXXGenerateTestOCRSecrets(), e.HomeChainSel, e.FeedChainSel, newChain, tokenConfig, types.PluginTypeCCIPExec) require.NoError(t, err) setCandidateForExecExec := SignProposal(t, e.Env, setCandidateForExecProposal) ExecuteProposal(t, e.Env, setCandidateForExecExec, state, e.HomeChainSel) diff --git a/integration-tests/deployment/ccip/add_lane_test.go b/integration-tests/deployment/ccip/add_lane_test.go index e63d1845774..040b7f781b0 100644 --- a/integration-tests/deployment/ccip/add_lane_test.go +++ b/integration-tests/deployment/ccip/add_lane_test.go @@ -16,7 +16,7 @@ func TestAddLane(t *testing.T) { // TODO: The offchain code doesn't yet support partial lane // enablement, need to address then re-enable this test. t.Skip() - e := NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 3) + e := NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 3, 4) // Here we have CR + nodes set up, but no CCIP contracts deployed. state, err := LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) diff --git a/integration-tests/deployment/ccip/changeset/active_candidate_changeset_test.go b/integration-tests/deployment/ccip/changeset/active_candidate_changeset_test.go new file mode 100644 index 00000000000..155eba35d91 --- /dev/null +++ b/integration-tests/deployment/ccip/changeset/active_candidate_changeset_test.go @@ -0,0 +1,253 @@ +package changeset + +import ( + "testing" + + "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" + + cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink-ccip/pluginconfig" + "github.com/smartcontractkit/chainlink/integration-tests/deployment" + + "github.com/stretchr/testify/require" + + jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" + ccdeploy "github.com/smartcontractkit/chainlink/integration-tests/deployment/ccip" + + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestActiveCandidate(t *testing.T) { + lggr := logger.TestLogger(t) + ctx := ccdeploy.Context(t) + tenv := ccdeploy.NewMemoryEnvironment(t, lggr, 3, 5) + e := tenv.Env + + state, err := ccdeploy.LoadOnchainState(tenv.Env, tenv.Ab) + require.NoError(t, err) + require.NotNil(t, state.Chains[tenv.HomeChainSel].LinkToken) + + feeds := state.Chains[tenv.FeedChainSel].USDFeeds + tokenConfig := ccdeploy.NewTokenConfig() + + tokenConfig.UpsertTokenInfo(ccdeploy.LinkSymbol, + pluginconfig.TokenInfo{ + AggregatorAddress: cciptypes.UnknownEncodedAddress(feeds[ccdeploy.LinkSymbol].Address().String()), + Decimals: ccdeploy.LinkDecimals, + DeviationPPB: cciptypes.NewBigIntFromInt64(1e9), + }, + ) + tokenConfig.UpsertTokenInfo(ccdeploy.WethSymbol, + pluginconfig.TokenInfo{ + AggregatorAddress: cciptypes.UnknownEncodedAddress(feeds[ccdeploy.WethSymbol].Address().String()), + Decimals: ccdeploy.WethDecimals, + DeviationPPB: cciptypes.NewBigIntFromInt64(4e9), + }, + ) + + output, err := InitialDeployChangeSet(tenv.Ab, tenv.Env, ccdeploy.DeployCCIPContractConfig{ + HomeChainSel: tenv.HomeChainSel, + FeedChainSel: tenv.FeedChainSel, + ChainsToDeploy: tenv.Env.AllChainSelectors(), + TokenConfig: tokenConfig, + MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e), + FeeTokenContracts: tenv.FeeTokenContracts, + CapabilityRegistry: state.Chains[tenv.HomeChainSel].CapabilityRegistry.Address(), + OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + }) + require.NoError(t, err) + // Get new state after migration. + state, err = ccdeploy.LoadOnchainState(e, tenv.Ab) + require.NoError(t, err) + homeCS, destCS := tenv.HomeChainSel, tenv.FeedChainSel + + // Ensure capreg logs are up to date. + ccdeploy.ReplayLogs(t, e.Offchain, tenv.ReplayBlocks) + + // Apply the jobs. + for nodeID, jobs := range output.JobSpecs { + for _, job := range jobs { + // Note these auto-accept + _, err := e.Offchain.ProposeJob(ctx, + &jobv1.ProposeJobRequest{ + NodeId: nodeID, + Spec: job, + }) + require.NoError(t, err) + } + } + + // Add all lanes + require.NoError(t, ccdeploy.AddLanesForAll(e, state)) + // Need to keep track of the block number for each chain so that event subscription can be done from that block. + startBlocks := make(map[uint64]*uint64) + // Send a message from each chain to every other chain. + expectedSeqNum := make(map[uint64]uint64) + for src := range e.Chains { + for dest, destChain := range e.Chains { + if src == dest { + continue + } + latesthdr, err := destChain.Client.HeaderByNumber(testcontext.Get(t), nil) + require.NoError(t, err) + block := latesthdr.Number.Uint64() + startBlocks[dest] = &block + seqNum := ccdeploy.SendRequest(t, e, state, src, dest, false) + expectedSeqNum[dest] = seqNum + } + } + + // Wait for all commit reports to land. + ccdeploy.ConfirmCommitForAllWithExpectedSeqNums(t, e, state, expectedSeqNum, startBlocks) + + //After commit is reported on all chains, token prices should be updated in FeeQuoter. + for dest := range e.Chains { + linkAddress := state.Chains[dest].LinkToken.Address() + feeQuoter := state.Chains[dest].FeeQuoter + timestampedPrice, err := feeQuoter.GetTokenPrice(nil, linkAddress) + require.NoError(t, err) + require.Equal(t, ccdeploy.MockLinkPrice, timestampedPrice.Value) + } + + //Wait for all exec reports to land + ccdeploy.ConfirmExecWithSeqNrForAll(t, e, state, expectedSeqNum, startBlocks) + + // transfer ownership + ccdeploy.TransferAllOwnership(t, state, homeCS, e) + acceptOwnershipProposal, err := ccdeploy.GenerateAcceptOwnershipProposal(state, homeCS, e.AllChainSelectors()) + require.NoError(t, err) + acceptOwnershipExec := ccdeploy.SignProposal(t, e, acceptOwnershipProposal) + for _, sel := range e.AllChainSelectors() { + ccdeploy.ExecuteProposal(t, e, acceptOwnershipExec, state, sel) + } + // Apply the accept ownership proposal to all the chains. + + err = ccdeploy.ConfirmRequestOnSourceAndDest(t, e, state, homeCS, destCS, 2) + require.NoError(t, err) + + // [ACTIVE, CANDIDATE] setup by setting candidate through cap reg + capReg, ccipHome := state.Chains[homeCS].CapabilityRegistry, state.Chains[homeCS].CCIPHome + donID, err := ccdeploy.DonIDForChain(capReg, ccipHome, destCS) + require.NoError(t, err) + donInfo, err := state.Chains[homeCS].CapabilityRegistry.GetDON(nil, donID) + require.NoError(t, err) + require.Equal(t, 5, len(donInfo.NodeP2PIds)) + require.Equal(t, uint32(4), donInfo.ConfigCount) + + state, err = ccdeploy.LoadOnchainState(e, tenv.Ab) + require.NoError(t, err) + + // delete a non-bootstrap node + nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) + require.NoError(t, err) + var newNodeIDs []string + // make sure we delete a node that is NOT bootstrap. + // we will remove bootstrap later by calling nodes.NonBootstrap() + if nodes[0].IsBootstrap { + newNodeIDs = e.NodeIDs[:len(e.NodeIDs)-1] + } else { + newNodeIDs = e.NodeIDs[1:] + } + nodes, err = deployment.NodeInfo(newNodeIDs, e.Offchain) + require.NoError(t, err) + + // this will construct ocr3 configurations for the + // commit and exec plugin we will be using + rmnHomeAddress := state.Chains[homeCS].RMNHome.Address() + ocr3ConfigMap, err := ccdeploy.BuildOCR3ConfigForCCIPHome( + e.Logger, + deployment.XXXGenerateTestOCRSecrets(), + state.Chains[destCS].OffRamp, + e.Chains[destCS], + destCS, + tokenConfig.GetTokenInfo(e.Logger, state.Chains[destCS].LinkToken, state.Chains[destCS].Weth9), + nodes.NonBootstraps(), + rmnHomeAddress, + ) + require.NoError(t, err) + + setCommitCandidateOp, err := ccdeploy.SetCandidateOnExistingDon( + ocr3ConfigMap[cctypes.PluginTypeCCIPCommit], + state.Chains[homeCS].CapabilityRegistry, + state.Chains[homeCS].CCIPHome, + destCS, + nodes.NonBootstraps(), + ) + require.NoError(t, err) + setCommitCandidateProposal, err := ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ + ChainIdentifier: mcms.ChainIdentifier(homeCS), + Batch: setCommitCandidateOp, + }}, "set new candidates on commit plugin", 0) + require.NoError(t, err) + setCommitCandidateSigned := ccdeploy.SignProposal(t, e, setCommitCandidateProposal) + ccdeploy.ExecuteProposal(t, e, setCommitCandidateSigned, state, homeCS) + + // create the op for the commit plugin as well + setExecCandidateOp, err := ccdeploy.SetCandidateOnExistingDon( + ocr3ConfigMap[cctypes.PluginTypeCCIPExec], + state.Chains[homeCS].CapabilityRegistry, + state.Chains[homeCS].CCIPHome, + destCS, + nodes.NonBootstraps(), + ) + require.NoError(t, err) + + setExecCandidateProposal, err := ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ + ChainIdentifier: mcms.ChainIdentifier(homeCS), + Batch: setExecCandidateOp, + }}, "set new candidates on commit and exec plugins", 0) + require.NoError(t, err) + setExecCandidateSigned := ccdeploy.SignProposal(t, e, setExecCandidateProposal) + ccdeploy.ExecuteProposal(t, e, setExecCandidateSigned, state, homeCS) + + // check setup was successful by confirming number of nodes from cap reg + donInfo, err = state.Chains[homeCS].CapabilityRegistry.GetDON(nil, donID) + require.NoError(t, err) + require.Equal(t, 4, len(donInfo.NodeP2PIds)) + require.Equal(t, uint32(6), donInfo.ConfigCount) + // [ACTIVE, CANDIDATE] done setup + + // [ACTIVE, CANDIDATE] make sure we can still send successful transaction without updating job specs + err = ccdeploy.ConfirmRequestOnSourceAndDest(t, e, state, homeCS, destCS, 3) + require.NoError(t, err) + // [ACTIVE, CANDIDATE] done send successful transaction on active + + // [NEW ACTIVE, NO CANDIDATE] promote to active + // confirm by getting old candidate digest and making sure new active matches + oldCandidateDigest, err := state.Chains[homeCS].CCIPHome.GetCandidateDigest(nil, donID, uint8(cctypes.PluginTypeCCIPExec)) + require.NoError(t, err) + + promoteOps, err := ccdeploy.PromoteAllCandidatesForChainOps(state.Chains[homeCS].CapabilityRegistry, state.Chains[homeCS].CCIPHome, destCS, nodes.NonBootstraps()) + require.NoError(t, err) + promoteProposal, err := ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ + ChainIdentifier: mcms.ChainIdentifier(homeCS), + Batch: promoteOps, + }}, "promote candidates and revoke actives", 0) + require.NoError(t, err) + promoteSigned := ccdeploy.SignProposal(t, e, promoteProposal) + ccdeploy.ExecuteProposal(t, e, promoteSigned, state, homeCS) + // [NEW ACTIVE, NO CANDIDATE] done promoting + + // [NEW ACTIVE, NO CANDIDATE] check onchain state + newActiveDigest, err := state.Chains[homeCS].CCIPHome.GetActiveDigest(nil, donID, uint8(cctypes.PluginTypeCCIPExec)) + require.NoError(t, err) + require.Equal(t, oldCandidateDigest, newActiveDigest) + + newCandidateDigest, err := state.Chains[homeCS].CCIPHome.GetCandidateDigest(nil, donID, uint8(cctypes.PluginTypeCCIPCommit)) + require.NoError(t, err) + require.Equal(t, newCandidateDigest, [32]byte{}) + // [NEW ACTIVE, NO CANDIDATE] done checking on chain state + + // [NEW ACTIVE, NO CANDIDATE] send successful request on new active + donInfo, err = state.Chains[homeCS].CapabilityRegistry.GetDON(nil, donID) + require.NoError(t, err) + require.Equal(t, uint32(8), donInfo.ConfigCount) + + err = ccdeploy.ConfirmRequestOnSourceAndDest(t, e, state, homeCS, destCS, 4) + require.NoError(t, err) + // [NEW ACTIVE, NO CANDIDATE] done sending successful request +} diff --git a/integration-tests/deployment/ccip/changeset/initial_deploy_test.go b/integration-tests/deployment/ccip/changeset/initial_deploy_test.go index 704481f60ff..844a3be715e 100644 --- a/integration-tests/deployment/ccip/changeset/initial_deploy_test.go +++ b/integration-tests/deployment/ccip/changeset/initial_deploy_test.go @@ -21,7 +21,7 @@ import ( func TestInitialDeploy(t *testing.T) { lggr := logger.TestLogger(t) ctx := ccdeploy.Context(t) - tenv := ccdeploy.NewMemoryEnvironment(t, lggr, 3) + tenv := ccdeploy.NewMemoryEnvironment(t, lggr, 3, 4) e := tenv.Env state, err := ccdeploy.LoadOnchainState(tenv.Env, tenv.Ab) diff --git a/integration-tests/deployment/ccip/deploy_home_chain.go b/integration-tests/deployment/ccip/deploy_home_chain.go index 24057863acc..ad1b451b0c3 100644 --- a/integration-tests/deployment/ccip/deploy_home_chain.go +++ b/integration-tests/deployment/ccip/deploy_home_chain.go @@ -417,6 +417,7 @@ func DonIDForChain(registry *capabilities_registry.CapabilitiesRegistry, ccipHom if err != nil { return 0, err } + // TODO: what happens if there are multiple dons for one chain (accidentally?) for _, don := range dons { if len(don.CapabilityConfigurations) == 1 && don.CapabilityConfigurations[0].CapabilityId == CCIPCapabilityID { @@ -646,9 +647,9 @@ func setupExecDON( } // SetCandidateExecPluginOps calls setCandidate on CCIPHome contract through the UpdateDON call on CapReg contract -// This proposes to set up OCR3 config for the exec plugin for the DON -func SetCandidateExecPluginOps( - execConfig ccip_home.CCIPHomeOCR3Config, +// This proposes to set up OCR3 config for the provided plugin for the DON +func SetCandidateOnExistingDon( + pluginConfig ccip_home.CCIPHomeOCR3Config, capReg *capabilities_registry.CapabilitiesRegistry, ccipHome *ccip_home.CCIPHome, chainSelector uint64, @@ -659,11 +660,12 @@ func SetCandidateExecPluginOps( if err != nil { return nil, fmt.Errorf("fetch don id for chain: %w", err) } + fmt.Printf("donID: %d", donID) encodedSetCandidateCall, err := CCIPHomeABI.Pack( "setCandidate", donID, - execConfig.PluginType, - execConfig, + pluginConfig.PluginType, + pluginConfig, [32]byte{}, ) if err != nil { @@ -698,17 +700,17 @@ func SetCandidateExecPluginOps( // SetCandidateCommitPluginWithAddDonOps sets the candidate commit config by calling setCandidate on CCIPHome contract through the AddDON call on CapReg contract // This should be done first before calling any other UpdateDON calls // This proposes to set up OCR3 config for the commit plugin for the DON -func SetCandidateCommitPluginWithAddDonOps( +func NewDonWithCandidateOp( donID uint32, - commitConfig ccip_home.CCIPHomeOCR3Config, + pluginConfig ccip_home.CCIPHomeOCR3Config, capReg *capabilities_registry.CapabilitiesRegistry, nodes deployment.Nodes, ) (mcms.Operation, error) { encodedSetCandidateCall, err := CCIPHomeABI.Pack( "setCandidate", donID, - commitConfig.PluginType, - commitConfig, + pluginConfig.PluginType, + pluginConfig, [32]byte{}, ) if err != nil { @@ -730,8 +732,8 @@ func SetCandidateCommitPluginWithAddDonOps( }, nil } -// PromoteCandidateOps promotes the candidate commit and exec configs to active by calling promoteCandidateAndRevokeActive on CCIPHome through the UpdateDON call on CapReg contract -func PromoteCandidateOps( +// PromoteAllCandidatesForChainOps promotes the candidate commit and exec configs to active by calling promoteCandidateAndRevokeActive on CCIPHome through the UpdateDON call on CapReg contract +func PromoteAllCandidatesForChainOps( capReg *capabilities_registry.CapabilitiesRegistry, ccipHome *ccip_home.CCIPHome, chainSelector uint64, @@ -743,74 +745,48 @@ func PromoteCandidateOps( return nil, fmt.Errorf("fetch don id for chain: %w", err) } - mcmsOps := []mcms.Operation{} - - commitCandidateDigest, err := ccipHome.GetCandidateDigest(nil, donID, uint8(cctypes.PluginTypeCCIPCommit)) + var mcmsOps []mcms.Operation + updateCommitOp, err := promoteCandidateOp(donID, uint8(cctypes.PluginTypeCCIPCommit), capReg, ccipHome, nodes) if err != nil { - return nil, fmt.Errorf("get commit candidate digest: %w", err) + return nil, fmt.Errorf("promote candidate op: %w", err) } + mcmsOps = append(mcmsOps, updateCommitOp) - if commitCandidateDigest == [32]byte{} { - return nil, fmt.Errorf("candidate digest is empty, expected nonempty") - } - fmt.Printf("commit candidate digest after setCandidate: %x\n", commitCandidateDigest) - - encodedPromotionCall, err := CCIPHomeABI.Pack( - "promoteCandidateAndRevokeActive", - donID, - uint8(cctypes.PluginTypeCCIPCommit), - commitCandidateDigest, - [32]byte{}, - ) + updateExecOp, err := promoteCandidateOp(donID, uint8(cctypes.PluginTypeCCIPExec), capReg, ccipHome, nodes) if err != nil { - return nil, fmt.Errorf("pack promotion call: %w", err) + return nil, fmt.Errorf("promote candidate op: %w", err) } + mcmsOps = append(mcmsOps, updateExecOp) - updateDonTx, err := capReg.UpdateDON( - deployment.SimTransactOpts(), - donID, - nodes.PeerIDs(), - []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: CCIPCapabilityID, - Config: encodedPromotionCall, - }, - }, - false, - nodes.DefaultF(), - ) - if err != nil { - return nil, fmt.Errorf("update don w/ commit config: %w", err) - } - mcmsOps = append(mcmsOps, mcms.Operation{ - To: capReg.Address(), - Data: updateDonTx.Data(), - Value: big.NewInt(0), - }) + return mcmsOps, nil +} - // check that candidate digest is empty. - execCandidateDigest, err := ccipHome.GetCandidateDigest(nil, donID, uint8(cctypes.PluginTypeCCIPExec)) +// promoteCandidateOp will create the MCMS Operation for `promoteCandidateAndRevokeActive` directed towards the capabilityRegistry +func promoteCandidateOp(donID uint32, pluginType uint8, capReg *capabilities_registry.CapabilitiesRegistry, + ccipHome *ccip_home.CCIPHome, nodes deployment.Nodes) (mcms.Operation, error) { + + allConfigs, err := ccipHome.GetAllConfigs(nil, donID, pluginType) if err != nil { - return nil, fmt.Errorf("get exec candidate digest 1st time: %w", err) + return mcms.Operation{}, err } - if execCandidateDigest == [32]byte{} { - return nil, fmt.Errorf("candidate digest is empty, expected nonempty") + if allConfigs.CandidateConfig.ConfigDigest == [32]byte{} { + return mcms.Operation{}, fmt.Errorf("candidate digest is empty, expected nonempty") } + fmt.Printf("commit candidate digest after setCandidate: %x\n", allConfigs.CandidateConfig.ConfigDigest) - // promote candidate call - encodedPromotionCall, err = CCIPHomeABI.Pack( + encodedPromotionCall, err := CCIPHomeABI.Pack( "promoteCandidateAndRevokeActive", donID, - uint8(cctypes.PluginTypeCCIPExec), - execCandidateDigest, - [32]byte{}, + pluginType, + allConfigs.CandidateConfig.ConfigDigest, + allConfigs.ActiveConfig.ConfigDigest, ) if err != nil { - return nil, fmt.Errorf("pack promotion call: %w", err) + return mcms.Operation{}, fmt.Errorf("pack promotion call: %w", err) } - updateDonTx, err = capReg.UpdateDON( + updateDonTx, err := capReg.UpdateDON( deployment.SimTransactOpts(), donID, nodes.PeerIDs(), @@ -824,14 +800,13 @@ func PromoteCandidateOps( nodes.DefaultF(), ) if err != nil { - return nil, fmt.Errorf("update don w/ exec config: %w", err) + return mcms.Operation{}, fmt.Errorf("error creating updateDon op for donID(%d) and plugin type (%d): %w", donID, pluginType, err) } - mcmsOps = append(mcmsOps, mcms.Operation{ + return mcms.Operation{ To: capReg.Address(), Data: updateDonTx.Data(), Value: big.NewInt(0), - }) - return mcmsOps, nil + }, nil } // ValidateCCIPHomeConfigSetUp checks that the commit and exec active and candidate configs are set up correctly diff --git a/integration-tests/deployment/ccip/ownership.go b/integration-tests/deployment/ccip/ownership.go new file mode 100644 index 00000000000..75007c04ccf --- /dev/null +++ b/integration-tests/deployment/ccip/ownership.go @@ -0,0 +1,37 @@ +package ccipdeployment + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/integration-tests/deployment" +) + +func TransferAllOwnership(t *testing.T, state CCIPOnChainState, homeCS uint64, e deployment.Environment) { + for _, source := range e.AllChainSelectors() { + if state.Chains[source].OnRamp != nil { + tx, err := state.Chains[source].OnRamp.TransferOwnership(e.Chains[source].DeployerKey, state.Chains[source].Timelock.Address()) + require.NoError(t, err) + _, err = deployment.ConfirmIfNoError(e.Chains[source], tx, err) + require.NoError(t, err) + } + if state.Chains[source].FeeQuoter != nil { + tx, err := state.Chains[source].FeeQuoter.TransferOwnership(e.Chains[source].DeployerKey, state.Chains[source].Timelock.Address()) + require.NoError(t, err) + _, err = deployment.ConfirmIfNoError(e.Chains[source], tx, err) + require.NoError(t, err) + } + // TODO: add offramp and commit stores + + } + // Transfer CR contract ownership + tx, err := state.Chains[homeCS].CapabilityRegistry.TransferOwnership(e.Chains[homeCS].DeployerKey, state.Chains[homeCS].Timelock.Address()) + require.NoError(t, err) + _, err = deployment.ConfirmIfNoError(e.Chains[homeCS], tx, err) + require.NoError(t, err) + tx, err = state.Chains[homeCS].CCIPHome.TransferOwnership(e.Chains[homeCS].DeployerKey, state.Chains[homeCS].Timelock.Address()) + require.NoError(t, err) + _, err = deployment.ConfirmIfNoError(e.Chains[homeCS], tx, err) + require.NoError(t, err) +} diff --git a/integration-tests/deployment/ccip/propose.go b/integration-tests/deployment/ccip/propose.go index 33d073c5e67..ee7487569ad 100644 --- a/integration-tests/deployment/ccip/propose.go +++ b/integration-tests/deployment/ccip/propose.go @@ -4,8 +4,10 @@ import ( "bytes" "context" "crypto/ecdsa" + "fmt" "math/big" "testing" + "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -17,6 +19,8 @@ import ( chainsel "github.com/smartcontractkit/chain-selectors" "github.com/stretchr/testify/require" + mapset "github.com/deckarep/golang-set/v2" + "github.com/smartcontractkit/chainlink/integration-tests/deployment" ) @@ -84,7 +88,9 @@ func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Ex t.Log("Executing proposal on chain", sel) // Set the root. tx, err2 := executor.SetRootOnChain(env.Chains[sel].Client, env.Chains[sel].DeployerKey, mcms.ChainIdentifier(sel)) - require.NoError(t, err2) + if err2 != nil { + require.NoError(t, deployment.MaybeDataErr(err2)) + } _, err2 = env.Chains[sel].Confirm(tx) require.NoError(t, err2) @@ -134,8 +140,6 @@ func GenerateAcceptOwnershipProposal( ) (*timelock.MCMSWithTimelockProposal, error) { // TODO: Accept rest of contracts var batches []timelock.BatchChainOperation - metaDataPerChain := make(map[mcms.ChainIdentifier]mcms.ChainMetadata) - timelockAddresses := make(map[mcms.ChainIdentifier]common.Address) for _, sel := range chains { chain, _ := chainsel.ChainBySelector(sel) acceptOnRamp, err := state.Chains[sel].OnRamp.AcceptOwnership(deployment.SimTransactOpts()) @@ -147,15 +151,6 @@ func GenerateAcceptOwnershipProposal( return nil, err } chainSel := mcms.ChainIdentifier(chain.Selector) - opCount, err := state.Chains[sel].ProposerMcm.GetOpCount(nil) - if err != nil { - return nil, err - } - metaDataPerChain[chainSel] = mcms.ChainMetadata{ - MCMAddress: state.Chains[sel].ProposerMcm.Address(), - StartingOpCount: opCount.Uint64(), - } - timelockAddresses[chainSel] = state.Chains[sel].Timelock.Address() batches = append(batches, timelock.BatchChainOperation{ ChainIdentifier: chainSel, Batch: []mcms.Operation{ @@ -182,15 +177,6 @@ func GenerateAcceptOwnershipProposal( return nil, err } homeChainID := mcms.ChainIdentifier(homeChain) - opCount, err := state.Chains[homeChain].ProposerMcm.GetOpCount(nil) - if err != nil { - return nil, err - } - metaDataPerChain[homeChainID] = mcms.ChainMetadata{ - StartingOpCount: opCount.Uint64(), - MCMAddress: state.Chains[homeChain].ProposerMcm.Address(), - } - timelockAddresses[homeChainID] = state.Chains[homeChain].Timelock.Address() batches = append(batches, timelock.BatchChainOperation{ ChainIdentifier: homeChainID, Batch: []mcms.Operation{ @@ -207,16 +193,7 @@ func GenerateAcceptOwnershipProposal( }, }) - return timelock.NewMCMSWithTimelockProposal( - "1", - 2004259681, // TODO - []mcms.Signature{}, - false, - metaDataPerChain, - timelockAddresses, - "blah", // TODO - batches, - timelock.Schedule, "0s") + return BuildProposalFromBatches(state, batches, "accept ownership operations", 0) } func BuildProposalMetadata(state CCIPOnChainState, chains []uint64) (map[mcms.ChainIdentifier]common.Address, map[mcms.ChainIdentifier]mcms.ChainMetadata, error) { @@ -237,3 +214,34 @@ func BuildProposalMetadata(state CCIPOnChainState, chains []uint64) (map[mcms.Ch } return tlAddressMap, metaDataPerChain, nil } + +// Given batches of operations, we build the metadata and timelock addresses of those opartions +// We then return a proposal that can be executed and signed +func BuildProposalFromBatches(state CCIPOnChainState, batches []timelock.BatchChainOperation, description string, minDelay time.Duration) (*timelock.MCMSWithTimelockProposal, error) { + if len(batches) == 0 { + return nil, fmt.Errorf("no operations in batch") + } + + chains := mapset.NewSet[uint64]() + for _, op := range batches { + chains.Add(uint64(op.ChainIdentifier)) + } + + tls, mcmsMd, err := BuildProposalMetadata(state, chains.ToSlice()) + if err != nil { + return nil, err + } + + return timelock.NewMCMSWithTimelockProposal( + "1", + 2004259681, // TODO: should be parameterized and based on current block timestamp. + []mcms.Signature{}, + false, + mcmsMd, + tls, + description, + batches, + timelock.Schedule, + minDelay.String(), + ) +} diff --git a/integration-tests/deployment/ccip/propose_home_chain.go b/integration-tests/deployment/ccip/propose_home_chain.go index 8ad3e9cfffd..36444e40819 100644 --- a/integration-tests/deployment/ccip/propose_home_chain.go +++ b/integration-tests/deployment/ccip/propose_home_chain.go @@ -3,7 +3,6 @@ package ccipdeployment import ( "fmt" - "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" @@ -12,14 +11,14 @@ import ( ) // SetCandidateExecPluginProposal calls setCandidate on the CCIPHome for setting up OCR3 exec Plugin config for the new chain. -func SetCandidateExecPluginProposal( +func SetCandidatePluginProposal( state CCIPOnChainState, e deployment.Environment, nodes deployment.Nodes, ocrSecrets deployment.OCRSecrets, homeChainSel, feedChainSel, newChainSel uint64, tokenConfig TokenConfig, - rmnHomeAddress common.Address, + pluginType types.PluginType, ) (*timelock.MCMSWithTimelockProposal, error) { newDONArgs, err := BuildOCR3ConfigForCCIPHome( e.Logger, @@ -29,47 +28,32 @@ func SetCandidateExecPluginProposal( feedChainSel, tokenConfig.GetTokenInfo(e.Logger, state.Chains[newChainSel].LinkToken, state.Chains[newChainSel].Weth9), nodes.NonBootstraps(), - rmnHomeAddress, + state.Chains[homeChainSel].RMNHome.Address(), ) if err != nil { return nil, err } - execConfig, ok := newDONArgs[types.PluginTypeCCIPExec] + execConfig, ok := newDONArgs[pluginType] if !ok { return nil, fmt.Errorf("missing exec plugin in ocr3Configs") } - setCandidateMCMSOps, err := SetCandidateExecPluginOps( + setCandidateMCMSOps, err := SetCandidateOnExistingDon( execConfig, state.Chains[homeChainSel].CapabilityRegistry, state.Chains[homeChainSel].CCIPHome, newChainSel, nodes.NonBootstraps(), ) - - if err != nil { - return nil, err - } - timelockAddresses, metaDataPerChain, err := BuildProposalMetadata(state, []uint64{homeChainSel}) if err != nil { return nil, err } - return timelock.NewMCMSWithTimelockProposal( - "1", - 2004259681, // TODO: should be parameterized and based on current block timestamp. - []mcms.Signature{}, - false, - metaDataPerChain, - timelockAddresses, - "SetCandidate for execution", - []timelock.BatchChainOperation{{ - ChainIdentifier: mcms.ChainIdentifier(homeChainSel), - Batch: setCandidateMCMSOps, - }}, - timelock.Schedule, - "0s", // TODO: Should be parameterized. - ) + + return BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ + ChainIdentifier: mcms.ChainIdentifier(homeChainSel), + Batch: setCandidateMCMSOps, + }}, "SetCandidate for execution", 0) } // PromoteCandidateProposal generates a proposal to call promoteCandidate on the CCIPHome through CapReg. @@ -79,7 +63,7 @@ func PromoteCandidateProposal( homeChainSel, newChainSel uint64, nodes deployment.Nodes, ) (*timelock.MCMSWithTimelockProposal, error) { - promoteCandidateOps, err := PromoteCandidateOps( + promoteCandidateOps, err := PromoteAllCandidatesForChainOps( state.Chains[homeChainSel].CapabilityRegistry, state.Chains[homeChainSel].CCIPHome, newChainSel, diff --git a/integration-tests/deployment/ccip/test_helpers.go b/integration-tests/deployment/ccip/test_helpers.go index 58a7c64a551..3e34b3af1f5 100644 --- a/integration-tests/deployment/ccip/test_helpers.go +++ b/integration-tests/deployment/ccip/test_helpers.go @@ -159,8 +159,9 @@ func allocateCCIPChainSelectors(chains map[uint64]deployment.Chain) (homeChainSe // NewMemoryEnvironment creates a new CCIP environment // with capreg, fee tokens, feeds and nodes set up. -func NewMemoryEnvironment(t *testing.T, lggr logger.Logger, numChains int) DeployedEnv { +func NewMemoryEnvironment(t *testing.T, lggr logger.Logger, numChains int, numNodes int) DeployedEnv { require.GreaterOrEqual(t, numChains, 2, "numChains must be at least 2 for home and feed chains") + require.GreaterOrEqual(t, numNodes, 4, "numNodes must be at least 4") ctx := testcontext.Get(t) chains := memory.NewMemoryChains(t, numChains) homeChainSel, feedSel := allocateCCIPChainSelectors(chains) @@ -169,7 +170,7 @@ func NewMemoryEnvironment(t *testing.T, lggr logger.Logger, numChains int) Deplo ab := deployment.NewMemoryAddressBook() feeTokenContracts, crConfig := DeployTestContracts(t, lggr, ab, homeChainSel, feedSel, chains) - nodes := memory.NewNodes(t, zapcore.InfoLevel, chains, 4, 1, crConfig) + nodes := memory.NewNodes(t, zapcore.InfoLevel, chains, numNodes, 1, crConfig) for _, node := range nodes { require.NoError(t, node.App.Start(ctx)) t.Cleanup(func() { @@ -188,8 +189,8 @@ func NewMemoryEnvironment(t *testing.T, lggr logger.Logger, numChains int) Deplo } } -func NewMemoryEnvironmentWithJobs(t *testing.T, lggr logger.Logger, numChains int) DeployedEnv { - e := NewMemoryEnvironment(t, lggr, numChains) +func NewMemoryEnvironmentWithJobs(t *testing.T, lggr logger.Logger, numChains int, numNodes int) DeployedEnv { + e := NewMemoryEnvironment(t, lggr, numChains, numNodes) e.SetupJobs(t) return e } @@ -559,3 +560,25 @@ func deploySingleFeed( return mockTokenFeed.Address, desc, nil } + +func ConfirmRequestOnSourceAndDest(t *testing.T, env deployment.Environment, state CCIPOnChainState, sourceCS, destCS, expectedSeqNr uint64) error { + latesthdr, err := env.Chains[destCS].Client.HeaderByNumber(testcontext.Get(t), nil) + require.NoError(t, err) + startBlock := latesthdr.Number.Uint64() + fmt.Printf("startblock %d", startBlock) + seqNum := SendRequest(t, env, state, sourceCS, destCS, false) + require.Equal(t, expectedSeqNr, seqNum) + + fmt.Printf("Request sent for seqnr %d", seqNum) + require.NoError(t, + ConfirmCommitWithExpectedSeqNumRange(t, env.Chains[sourceCS], env.Chains[destCS], state.Chains[destCS].OffRamp, &startBlock, cciptypes.SeqNumRange{ + cciptypes.SeqNum(seqNum), + cciptypes.SeqNum(seqNum), + })) + + 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)) + + return nil +} diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 4eae80919bd..e79bf15ad0c 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -15,6 +15,7 @@ require ( github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df github.com/chaos-mesh/chaos-mesh/api v0.0.0-20240821051457-da69c6d9617a github.com/cli/go-gh/v2 v2.0.0 + github.com/deckarep/golang-set/v2 v2.6.0 github.com/ethereum/go-ethereum v1.13.8 github.com/fxamacker/cbor/v2 v2.7.0 github.com/go-resty/resty/v2 v2.13.1 @@ -175,7 +176,6 @@ require ( github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect