From 946a725e572f32683cf01cf89721300c65e070dd Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 18 Nov 2024 10:58:33 -0500 Subject: [PATCH 01/13] Extract MCMS --- .../ccip/changeset/active_candidate_test.go | 18 +-- deployment/ccip/changeset/add_chain_test.go | 8 +- deployment/ccip/changeset/home_chain.go | 1 - .../ccip/changeset/initial_deploy_test.go | 2 +- deployment/ccip/deploy.go | 126 ++------------- deployment/ccip/deploy_test.go | 1 - deployment/ccip/propose.go | 121 +-------------- deployment/ccip/state.go | 51 ++---- deployment/ccip/view/types/contract_state.go | 33 ---- deployment/ccip/view/view.go | 2 + .../changeset/deploy_mcms_with_timelock.go | 32 ++++ deployment/common/changeset/internal/mcms.go | 138 +++++++++++++++++ .../common/changeset/internal/mcms_test.go | 54 +++++++ .../common/changeset/mcms_test_helpers.go | 115 ++++++++++++++ deployment/common/changeset/state.go | 108 +++++++++++++ deployment/common/view/v1_0/mcms.go | 146 ++++++++++++++++++ 16 files changed, 633 insertions(+), 323 deletions(-) delete mode 100644 deployment/ccip/view/types/contract_state.go create mode 100644 deployment/common/changeset/deploy_mcms_with_timelock.go create mode 100644 deployment/common/changeset/internal/mcms.go create mode 100644 deployment/common/changeset/internal/mcms_test.go create mode 100644 deployment/common/changeset/mcms_test_helpers.go create mode 100644 deployment/common/changeset/state.go create mode 100644 deployment/common/view/v1_0/mcms.go diff --git a/deployment/ccip/changeset/active_candidate_test.go b/deployment/ccip/changeset/active_candidate_test.go index 9daf383c971..9386ca9f93d 100644 --- a/deployment/ccip/changeset/active_candidate_test.go +++ b/deployment/ccip/changeset/active_candidate_test.go @@ -19,6 +19,7 @@ import ( jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" + commondeploy "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -43,7 +44,6 @@ func TestActiveCandidate(t *testing.T) { FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), TokenConfig: tokenConfig, - MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e), OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), }) require.NoError(t, err) @@ -114,9 +114,9 @@ func TestActiveCandidate(t *testing.T) { ccdeploy.TransferAllOwnership(t, state, homeCS, e) acceptOwnershipProposal, err := ccdeploy.GenerateAcceptOwnershipProposal(state, homeCS, e.AllChainSelectors()) require.NoError(t, err) - acceptOwnershipExec := ccdeploy.SignProposal(t, e, acceptOwnershipProposal) + acceptOwnershipExec := commondeploy.SignProposal(t, e, acceptOwnershipProposal) for _, sel := range e.AllChainSelectors() { - ccdeploy.ExecuteProposal(t, e, acceptOwnershipExec, state, sel) + commondeploy.ExecuteProposal(t, e, acceptOwnershipExec, state.Chains[sel].Timelock, sel) } // Apply the accept ownership proposal to all the chains. @@ -176,8 +176,8 @@ func TestActiveCandidate(t *testing.T) { 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) + setCommitCandidateSigned := commondeploy.SignProposal(t, e, setCommitCandidateProposal) + commondeploy.ExecuteProposal(t, e, setCommitCandidateSigned, state.Chains[homeCS].Timelock, homeCS) // create the op for the commit plugin as well setExecCandidateOp, err := ccdeploy.SetCandidateOnExistingDon( @@ -194,8 +194,8 @@ func TestActiveCandidate(t *testing.T) { 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) + setExecCandidateSigned := commondeploy.SignProposal(t, e, setExecCandidateProposal) + commondeploy.ExecuteProposal(t, e, setExecCandidateSigned, state.Chains[homeCS].Timelock, homeCS) // check setup was successful by confirming number of nodes from cap reg donInfo, err = state.Chains[homeCS].CapabilityRegistry.GetDON(nil, donID) @@ -221,8 +221,8 @@ func TestActiveCandidate(t *testing.T) { 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) + promoteSigned := commondeploy.SignProposal(t, e, promoteProposal) + commondeploy.ExecuteProposal(t, e, promoteSigned, state.Chains[homeCS].Timelock, homeCS) // [NEW ACTIVE, NO CANDIDATE] done promoting // [NEW ACTIVE, NO CANDIDATE] check onchain state diff --git a/deployment/ccip/changeset/add_chain_test.go b/deployment/ccip/changeset/add_chain_test.go index 6a87bdd0a0a..69f66acb730 100644 --- a/deployment/ccip/changeset/add_chain_test.go +++ b/deployment/ccip/changeset/add_chain_test.go @@ -5,6 +5,7 @@ import ( "time" ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" + commondeployment "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" @@ -44,7 +45,6 @@ func TestAddChainInbound(t *testing.T) { FeedChainSel: e.FeedChainSel, ChainsToDeploy: initialDeploy, TokenConfig: tokenConfig, - MCMSConfig: ccipdeployment.NewTestMCMSConfig(t, e.Env), OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), }) require.NoError(t, err) @@ -74,7 +74,7 @@ func TestAddChainInbound(t *testing.T) { ccipdeployment.FeeTokenContracts{ LinkToken: state.Chains[newChain].LinkToken, Weth9: state.Chains[newChain].Weth9, - }, ccipdeployment.NewTestMCMSConfig(t, e.Env), rmnHome) + }, rmnHome) require.NoError(t, err) require.NoError(t, e.Env.ExistingAddresses.Merge(newChainAddresses)) state, err = ccipdeployment.LoadOnchainState(e.Env) @@ -112,10 +112,10 @@ func TestAddChainInbound(t *testing.T) { acceptOwnershipProposal, err := ccipdeployment.GenerateAcceptOwnershipProposal(state, e.HomeChainSel, initialDeploy) require.NoError(t, err) - acceptOwnershipExec := ccipdeployment.SignProposal(t, e.Env, acceptOwnershipProposal) + acceptOwnershipExec := commondeployment.SignProposal(t, e.Env, acceptOwnershipProposal) // Apply the accept ownership proposal to all the chains. for _, sel := range initialDeploy { - ccipdeployment.ExecuteProposal(t, e.Env, acceptOwnershipExec, state, sel) + commondeployment.ExecuteProposal(t, e.Env, acceptOwnershipExec, state.Chains[sel].Timelock, sel) } for _, chain := range initialDeploy { owner, err2 := state.Chains[chain].OnRamp.Owner(nil) diff --git a/deployment/ccip/changeset/home_chain.go b/deployment/ccip/changeset/home_chain.go index 7d7f64a8bb8..0fabd2efb18 100644 --- a/deployment/ccip/changeset/home_chain.go +++ b/deployment/ccip/changeset/home_chain.go @@ -17,7 +17,6 @@ var _ deployment.ChangeSet[DeployHomeChainConfig] = DeployHomeChain // DeployHomeChain is a separate changeset because it is a standalone deployment performed once in home chain for the entire CCIP deployment. func DeployHomeChain(env deployment.Environment, cfg DeployHomeChainConfig) (deployment.ChangesetOutput, error) { - err := cfg.Validate() if err != nil { return deployment.ChangesetOutput{}, errors.Wrapf(deployment.ErrInvalidConfig, "%v", err) diff --git a/deployment/ccip/changeset/initial_deploy_test.go b/deployment/ccip/changeset/initial_deploy_test.go index b7dbdfcc972..75df27edb5f 100644 --- a/deployment/ccip/changeset/initial_deploy_test.go +++ b/deployment/ccip/changeset/initial_deploy_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink/deployment" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" @@ -33,7 +34,6 @@ func TestInitialDeploy(t *testing.T) { FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), TokenConfig: ccdeploy.NewTestTokenConfig(state.Chains[tenv.FeedChainSel].USDFeeds), - MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e), OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), }) require.NoError(t, err) diff --git a/deployment/ccip/deploy.go b/deployment/ccip/deploy.go index 8a33cf0d26d..77acd61a147 100644 --- a/deployment/ccip/deploy.go +++ b/deployment/ccip/deploy.go @@ -7,11 +7,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" - owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" @@ -65,9 +62,6 @@ type DeployCCIPContractConfig struct { FeedChainSel uint64 ChainsToDeploy []uint64 TokenConfig TokenConfig - // I believe it makes sense to have the same signers across all chains - // since that's the point MCMS. - MCMSConfig MCMSConfig // For setting OCR configuration OCRSecrets deployment.OCRSecrets } @@ -140,7 +134,7 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c err = DeployChainContracts(e, chain, ab, FeeTokenContracts{ LinkToken: existingState.Chains[chainSel].LinkToken, Weth9: existingState.Chains[chainSel].Weth9, - }, c.MCMSConfig, rmnHome) + }, rmnHome) if err != nil { return err } @@ -189,113 +183,6 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c return nil } -type MCMSConfig struct { - Admin config.Config - Canceller config.Config - Bypasser config.Config - Proposer config.Config - Executors []common.Address -} - -func DeployMCMSWithConfig( - contractType deployment.ContractType, - lggr logger.Logger, - chain deployment.Chain, - ab deployment.AddressBook, - mcmConfig config.Config, -) (*deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig], error) { - groupQuorums, groupParents, signerAddresses, signerGroups := mcmConfig.ExtractSetConfigInputs() - mcm, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig] { - mcmAddr, tx, mcm, err2 := owner_helpers.DeployManyChainMultiSig( - chain.DeployerKey, - chain.Client, - ) - return deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig]{ - mcmAddr, mcm, tx, deployment.NewTypeAndVersion(contractType, deployment.Version1_0_0), err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy mcm", "err", err) - return mcm, err - } - mcmsTx, err := mcm.Contract.SetConfig(chain.DeployerKey, - signerAddresses, - signerGroups, // Signer 1 is int group 0 (root group) with quorum 1. - groupQuorums, - groupParents, - false, - ) - if _, err := deployment.ConfirmIfNoError(chain, mcmsTx, err); err != nil { - lggr.Errorw("Failed to confirm mcm config", "err", err) - return mcm, err - } - return mcm, nil -} - -type MCMSContracts struct { - Admin *deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig] - Canceller *deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig] - Bypasser *deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig] - Proposer *deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig] - Timelock *deployment.ContractDeploy[*owner_helpers.RBACTimelock] -} - -// DeployMCMSContracts deploys the MCMS contracts for the given configuration -// as well as the timelock. -func DeployMCMSContracts( - lggr logger.Logger, - chain deployment.Chain, - ab deployment.AddressBook, - mcmConfig MCMSConfig, -) (*MCMSContracts, error) { - adminMCM, err := DeployMCMSWithConfig(AdminManyChainMultisig, lggr, chain, ab, mcmConfig.Admin) - if err != nil { - return nil, err - } - bypasser, err := DeployMCMSWithConfig(BypasserManyChainMultisig, lggr, chain, ab, mcmConfig.Bypasser) - if err != nil { - return nil, err - } - canceller, err := DeployMCMSWithConfig(CancellerManyChainMultisig, lggr, chain, ab, mcmConfig.Canceller) - if err != nil { - return nil, err - } - proposer, err := DeployMCMSWithConfig(ProposerManyChainMultisig, lggr, chain, ab, mcmConfig.Proposer) - if err != nil { - return nil, err - } - - timelock, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*owner_helpers.RBACTimelock] { - timelock, tx2, cc, err2 := owner_helpers.DeployRBACTimelock( - chain.DeployerKey, - chain.Client, - big.NewInt(0), // minDelay - adminMCM.Address, - []common.Address{proposer.Address}, // proposers - mcmConfig.Executors, //executors - []common.Address{canceller.Address}, // cancellers - []common.Address{bypasser.Address}, // bypassers - ) - return deployment.ContractDeploy[*owner_helpers.RBACTimelock]{ - timelock, cc, tx2, deployment.NewTypeAndVersion(RBACTimelock, deployment.Version1_0_0), err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy timelock", "err", err) - return nil, err - } - lggr.Infow("deployed timelock", "addr", timelock.Address) - return &MCMSContracts{ - Admin: adminMCM, - Canceller: canceller, - Bypasser: bypasser, - Proposer: proposer, - Timelock: timelock, - }, nil -} - func DeployFeeTokensToChains(lggr logger.Logger, ab deployment.AddressBook, chains map[uint64]deployment.Chain) error { for _, chain := range chains { _, err := DeployFeeTokens(lggr, chain, ab) @@ -360,11 +247,16 @@ func DeployChainContracts( chain deployment.Chain, ab deployment.AddressBook, contractConfig FeeTokenContracts, - mcmsConfig MCMSConfig, rmnHome *rmn_home.RMNHome, ) error { - mcmsContracts, err := DeployMCMSContracts(e.Logger, chain, ab, mcmsConfig) + chainAddresses, err := e.ExistingAddresses.AddressesForChain(chain.Selector) + if err != nil { + e.Logger.Errorw("Failed to get chain addresses", "err", err) + return err + } + state, err := LoadChainState(chain, chainAddresses) if err != nil { + e.Logger.Errorw("Failed to load chain state", "err", err) return err } ccipReceiver, err := deployment.DeployContract(e.Logger, chain, ab, @@ -546,7 +438,7 @@ func DeployChainContracts( LinkToken: contractConfig.LinkToken.Address(), TokenPriceStalenessThreshold: uint32(24 * 60 * 60), }, - []common.Address{mcmsContracts.Timelock.Address}, // timelock should be able to update, ramps added after + []common.Address{state.Timelock.Address()}, // timelock should be able to update, ramps added after []common.Address{contractConfig.Weth9.Address(), contractConfig.LinkToken.Address()}, // fee tokens []fee_quoter.FeeQuoterTokenPriceFeedUpdate{}, []fee_quoter.FeeQuoterTokenTransferFeeConfigArgs{}, // TODO: tokens diff --git a/deployment/ccip/deploy_test.go b/deployment/ccip/deploy_test.go index 63aeacb4bdf..5842af25303 100644 --- a/deployment/ccip/deploy_test.go +++ b/deployment/ccip/deploy_test.go @@ -50,7 +50,6 @@ func TestDeployCCIPContracts(t *testing.T) { FeedChainSel: feedChainSel, ChainsToDeploy: e.AllChainSelectors(), TokenConfig: NewTokenConfig(), - MCMSConfig: NewTestMCMSConfig(t, e), OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), }) require.NoError(t, err) diff --git a/deployment/ccip/propose.go b/deployment/ccip/propose.go index 9d6ac417968..d7baf9ab542 100644 --- a/deployment/ccip/propose.go +++ b/deployment/ccip/propose.go @@ -1,138 +1,19 @@ package ccipdeployment import ( - "bytes" - "context" - "crypto/ecdsa" "fmt" "math/big" - "testing" "time" - "github.com/ethereum/go-ethereum/accounts/abi/bind" + mapset "github.com/deckarep/golang-set/v2" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" - owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" chainsel "github.com/smartcontractkit/chain-selectors" - "github.com/stretchr/testify/require" - - mapset "github.com/deckarep/golang-set/v2" "github.com/smartcontractkit/chainlink/deployment" ) -var ( - TestXXXMCMSSigner *ecdsa.PrivateKey -) - -func init() { - key, err := crypto.GenerateKey() - if err != nil { - panic(err) - } - TestXXXMCMSSigner = key -} - -func SingleGroupMCMS(t *testing.T) config.Config { - publicKey := TestXXXMCMSSigner.Public().(*ecdsa.PublicKey) - // Convert the public key to an Ethereum address - address := crypto.PubkeyToAddress(*publicKey) - c, err := config.NewConfig(1, []common.Address{address}, []config.Config{}) - require.NoError(t, err) - return *c -} - -func NewTestMCMSConfig(t *testing.T, e deployment.Environment) MCMSConfig { - c := SingleGroupMCMS(t) - // All deployer keys can execute. - var executors []common.Address - for _, chain := range e.Chains { - executors = append(executors, chain.DeployerKey.From) - } - return MCMSConfig{ - Admin: c, - Bypasser: c, - Canceller: c, - Executors: executors, - Proposer: c, - } -} - -func SignProposal(t *testing.T, env deployment.Environment, proposal *timelock.MCMSWithTimelockProposal) *mcms.Executor { - executorClients := make(map[mcms.ChainIdentifier]mcms.ContractDeployBackend) - for _, chain := range env.Chains { - chainselc, exists := chainsel.ChainBySelector(chain.Selector) - require.True(t, exists) - chainSel := mcms.ChainIdentifier(chainselc.Selector) - executorClients[chainSel] = chain.Client - } - executor, err := proposal.ToExecutor(true) - require.NoError(t, err) - payload, err := executor.SigningHash() - require.NoError(t, err) - // Sign the payload - sig, err := crypto.Sign(payload.Bytes(), TestXXXMCMSSigner) - require.NoError(t, err) - mcmSig, err := mcms.NewSignatureFromBytes(sig) - require.NoError(t, err) - executor.Proposal.AddSignature(mcmSig) - require.NoError(t, executor.Proposal.Validate()) - return executor -} - -func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Executor, - state CCIPOnChainState, sel uint64) { - 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)) - if err2 != nil { - require.NoError(t, deployment.MaybeDataErr(err2)) - } - _, err2 = env.Chains[sel].Confirm(tx) - require.NoError(t, err2) - - // TODO: This sort of helper probably should move to the MCMS lib. - // Execute all the transactions in the proposal which are for this chain. - for _, chainOp := range executor.Operations[mcms.ChainIdentifier(sel)] { - for idx, op := range executor.ChainAgnosticOps { - if bytes.Equal(op.Data, chainOp.Data) && op.To == chainOp.To { - opTx, err3 := executor.ExecuteOnChain(env.Chains[sel].Client, env.Chains[sel].DeployerKey, idx) - require.NoError(t, err3) - block, err3 := env.Chains[sel].Confirm(opTx) - require.NoError(t, err3) - t.Log("executed", chainOp) - it, err3 := state.Chains[sel].Timelock.FilterCallScheduled(&bind.FilterOpts{ - Start: block, - End: &block, - Context: context.Background(), - }, nil, nil) - require.NoError(t, err3) - var calls []owner_helpers.RBACTimelockCall - var pred, salt [32]byte - for it.Next() { - // Note these are the same for the whole batch, can overwrite - pred = it.Event.Predecessor - salt = it.Event.Salt - t.Log("scheduled", it.Event) - calls = append(calls, owner_helpers.RBACTimelockCall{ - Target: it.Event.Target, - Data: it.Event.Data, - Value: it.Event.Value, - }) - } - tx, err := state.Chains[sel].Timelock.ExecuteBatch( - env.Chains[sel].DeployerKey, calls, pred, salt) - require.NoError(t, err) - _, err = env.Chains[sel].Confirm(tx) - require.NoError(t, err) - } - } - } -} - func GenerateAcceptOwnershipProposal( state CCIPOnChainState, homeChain uint64, diff --git a/deployment/ccip/state.go b/deployment/ccip/state.go index 650f46d2b3a..271eaa716f0 100644 --- a/deployment/ccip/state.go +++ b/deployment/ccip/state.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_2" "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_5" "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_6" + commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" common_v1_0 "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" @@ -26,8 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" - owner_wrappers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/nonce_manager" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" @@ -42,6 +41,7 @@ import ( // CCIPChainState holds a Go binding for all the currently deployed CCIP contracts // on a chain. If a binding is nil, it means here is no such contract on the chain. type CCIPChainState struct { + commoncs.MCMSWithTimelockState OnRamp *onramp.OnRamp OffRamp *offramp.OffRamp FeeQuoter *fee_quoter.FeeQuoter @@ -68,11 +68,6 @@ type CCIPChainState struct { CapabilityRegistry *capabilities_registry.CapabilitiesRegistry CCIPHome *ccip_home.CCIPHome RMNHome *rmn_home.RMNHome - AdminMcm *owner_wrappers.ManyChainMultiSig - BypasserMcm *owner_wrappers.ManyChainMultiSig - CancellerMcm *owner_wrappers.ManyChainMultiSig - ProposerMcm *owner_wrappers.ManyChainMultiSig - Timelock *owner_wrappers.RBACTimelock // TODO remove once staging upgraded. CCIPConfig *ccip_config.CCIPConfig @@ -164,6 +159,13 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) { } chainView.CapabilityRegistry[c.CapabilityRegistry.Address().Hex()] = capRegView } + if c.MCMSWithTimelockState.Timelock != nil { + mcmsView, err := c.MCMSWithTimelockState.GenerateMCMSWithTimelockView() + if err != nil { + return chainView, err + } + chainView.MCMSWithTimelock = mcmsView + } return chainView, nil } @@ -229,38 +231,13 @@ func LoadOnchainState(e deployment.Environment) (CCIPOnChainState, error) { // Modifies map in place func LoadChainState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (CCIPChainState, error) { var state CCIPChainState + mcmsWithTimelock, err := commoncs.LoadMCMSWithTimelockState(chain, addresses) + if err != nil { + return state, err + } + state.MCMSWithTimelockState = *mcmsWithTimelock for address, tvStr := range addresses { switch tvStr.String() { - case deployment.NewTypeAndVersion(RBACTimelock, deployment.Version1_0_0).String(): - tl, err := owner_wrappers.NewRBACTimelock(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.Timelock = tl - case deployment.NewTypeAndVersion(AdminManyChainMultisig, deployment.Version1_0_0).String(): - mcms, err := owner_wrappers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.AdminMcm = mcms - case deployment.NewTypeAndVersion(ProposerManyChainMultisig, deployment.Version1_0_0).String(): - mcms, err := owner_wrappers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.ProposerMcm = mcms - case deployment.NewTypeAndVersion(BypasserManyChainMultisig, deployment.Version1_0_0).String(): - mcms, err := owner_wrappers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.BypasserMcm = mcms - case deployment.NewTypeAndVersion(CancellerManyChainMultisig, deployment.Version1_0_0).String(): - mcms, err := owner_wrappers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.CancellerMcm = mcms case deployment.NewTypeAndVersion(CapabilitiesRegistry, deployment.Version1_0_0).String(): cr, err := capabilities_registry.NewCapabilitiesRegistry(common.HexToAddress(address), chain.Client) if err != nil { diff --git a/deployment/ccip/view/types/contract_state.go b/deployment/ccip/view/types/contract_state.go deleted file mode 100644 index f65c510af53..00000000000 --- a/deployment/ccip/view/types/contract_state.go +++ /dev/null @@ -1,33 +0,0 @@ -package types - -import ( - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" -) - -type ContractMetaData struct { - TypeAndVersion string `json:"typeAndVersion,omitempty"` - Address common.Address `json:"address,omitempty"` - Owner common.Address `json:"owner,omitempty"` -} - -func NewContractMetaData(tv Meta, addr common.Address) (ContractMetaData, error) { - tvStr, err := tv.TypeAndVersion(nil) - if err != nil { - return ContractMetaData{}, err - } - owner, err := tv.Owner(nil) - if err != nil { - return ContractMetaData{}, err - } - return ContractMetaData{ - TypeAndVersion: tvStr, - Address: addr, - Owner: owner, - }, nil -} - -type Meta interface { - TypeAndVersion(opts *bind.CallOpts) (string, error) - Owner(opts *bind.CallOpts) (common.Address, error) -} diff --git a/deployment/ccip/view/view.go b/deployment/ccip/view/view.go index 9ef8583bdf6..318e09100b9 100644 --- a/deployment/ccip/view/view.go +++ b/deployment/ccip/view/view.go @@ -26,6 +26,7 @@ type ChainView struct { OnRamp map[string]v1_6.OnRampView `json:"onRamp,omitempty"` OffRamp map[string]v1_6.OffRampView `json:"offRamp,omitempty"` CapabilityRegistry map[string]common_v1_0.CapabilityRegistryView `json:"capabilityRegistry,omitempty"` + MCMSWithTimelock common_v1_0.MCMSWithTimelockView `json:"mcmsWithTimelock,omitempty"` } func NewChain() ChainView { @@ -44,6 +45,7 @@ func NewChain() ChainView { OnRamp: make(map[string]v1_6.OnRampView), OffRamp: make(map[string]v1_6.OffRampView), CapabilityRegistry: make(map[string]common_v1_0.CapabilityRegistryView), + MCMSWithTimelock: common_v1_0.MCMSWithTimelockView{}, } } diff --git a/deployment/common/changeset/deploy_mcms_with_timelock.go b/deployment/common/changeset/deploy_mcms_with_timelock.go new file mode 100644 index 00000000000..88caea1c125 --- /dev/null +++ b/deployment/common/changeset/deploy_mcms_with_timelock.go @@ -0,0 +1,32 @@ +package changeset + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal" +) + +var _ deployment.ChangeSet[map[uint64]MCMSWithTimelockConfig] = DeployMCMSWithTimelock + +type MCMSWithTimelockConfig struct { + Canceller config.Config + Bypasser config.Config + Proposer config.Config + TimelockExecutors []common.Address + TimelockMinDelay *big.Int +} + +func DeployMCMSWithTimelock(e deployment.Environment, cfgByChain map[uint64]MCMSWithTimelockConfig) (deployment.ChangesetOutput, error) { + newAddresses := deployment.NewMemoryAddressBook() + err := internal.DeployMCMSWithTimelockContractsBatch( + e.Logger, e.Chains, newAddresses, cfgByChain, + ) + if err != nil { + return deployment.ChangesetOutput{}, err + } + return deployment.ChangesetOutput{AddressBook: newAddresses}, nil +} diff --git a/deployment/common/changeset/internal/mcms.go b/deployment/common/changeset/internal/mcms.go new file mode 100644 index 00000000000..cf7ef95e629 --- /dev/null +++ b/deployment/common/changeset/internal/mcms.go @@ -0,0 +1,138 @@ +package internal + +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" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/deployment/common/types" + "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" +) + +func DeployMCMSWithConfig( + contractType deployment.ContractType, + lggr logger.Logger, + chain deployment.Chain, + ab deployment.AddressBook, + mcmConfig config.Config, +) (*deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig], error) { + groupQuorums, groupParents, signerAddresses, signerGroups := mcmConfig.ExtractSetConfigInputs() + mcm, err := deployment.DeployContract[*owner_helpers.ManyChainMultiSig](lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig] { + mcmAddr, tx, mcm, err2 := owner_helpers.DeployManyChainMultiSig( + chain.DeployerKey, + chain.Client, + ) + return deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig]{ + mcmAddr, mcm, tx, deployment.NewTypeAndVersion(contractType, deployment.Version1_0_0), err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy mcm", "err", err) + return mcm, err + } + mcmsTx, err := mcm.Contract.SetConfig(chain.DeployerKey, + signerAddresses, + // Signer 1 is int group 0 (root group) with quorum 1. + signerGroups, + groupQuorums, + groupParents, + false, + ) + if _, err := deployment.ConfirmIfNoError(chain, mcmsTx, err); err != nil { + lggr.Errorw("Failed to confirm mcm config", "err", err) + return mcm, err + } + return mcm, nil +} + +// MCMSWithTimelockDeploy holds a bundle of MCMS contract deploys. +type MCMSWithTimelockDeploy struct { + Canceller *deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig] + Bypasser *deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig] + Proposer *deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig] + Timelock *deployment.ContractDeploy[*owner_helpers.RBACTimelock] +} + +func DeployMCMSWithTimelockContractsBatch( + lggr logger.Logger, + chains map[uint64]deployment.Chain, + ab deployment.AddressBook, + cfgByChain map[uint64]changeset.MCMSWithTimelockConfig, +) error { + for chainSel, cfg := range cfgByChain { + _, err := DeployMCMSWithTimelockContracts(lggr, chains[chainSel], ab, cfg) + if err != nil { + return err + } + } + return nil +} + +// DeployMCMSWithTimelockContracts deploys an MCMS for +// each of the timelock roles Bypasser, ProposerMcm, Canceller. +// MCMS contracts for the given configuration +// as well as the timelock. It's not necessarily the only way to use +// the timelock and MCMS, but its reasonable pattern. +func DeployMCMSWithTimelockContracts( + lggr logger.Logger, + chain deployment.Chain, + ab deployment.AddressBook, + config types.MCMSWithTimelockConfig, +) (*MCMSWithTimelockDeploy, error) { + bypasser, err := DeployMCMSWithConfig(BypasserManyChainMultisig, lggr, chain, ab, config.Bypasser) + if err != nil { + return nil, err + } + canceller, err := DeployMCMSWithConfig(CancellerManyChainMultisig, lggr, chain, ab, config.Canceller) + if err != nil { + return nil, err + } + proposer, err := DeployMCMSWithConfig(ProposerManyChainMultisig, lggr, chain, ab, config.Proposer) + if err != nil { + return nil, err + } + + timelock, err := deployment.DeployContract[*owner_helpers.RBACTimelock](lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*owner_helpers.RBACTimelock] { + timelock, tx2, cc, err2 := owner_helpers.DeployRBACTimelock( + chain.DeployerKey, + chain.Client, + config.TimelockMinDelay, + // Deployer is the initial admin. + // TODO: Could expose this as config? + // Or keep this enforced to follow the same pattern? + chain.DeployerKey.From, + []common.Address{proposer.Address}, // proposers + config.TimelockExecutors, //executors + []common.Address{canceller.Address}, // cancellers + []common.Address{bypasser.Address}, // bypassers + ) + return deployment.ContractDeploy[*owner_helpers.RBACTimelock]{ + timelock, cc, tx2, deployment.NewTypeAndVersion(RBACTimelock, deployment.Version1_0_0), err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy timelock", "err", err) + return nil, err + } + lggr.Infow("deployed timelock", "addr", timelock.Address) + // We grant the timelock the admin role on the MCMS contracts. + tx, err := timelock.Contract.GrantRole(chain.DeployerKey, + v1_0.ADMIN_ROLE.ID, timelock.Address) + if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { + lggr.Errorw("Failed to grant timelock admin role", "err", err) + return nil, err + } + // After the proposer cycle is validated, + // we can remove the deployer as an admin. + return &MCMSWithTimelockDeploy{ + Canceller: canceller, + Bypasser: bypasser, + Proposer: proposer, + Timelock: timelock, + }, nil +} diff --git a/deployment/common/changeset/internal/mcms_test.go b/deployment/common/changeset/internal/mcms_test.go new file mode 100644 index 00000000000..d0245946863 --- /dev/null +++ b/deployment/common/changeset/internal/mcms_test.go @@ -0,0 +1,54 @@ +package internal + +import ( + "encoding/json" + "testing" + + "github.com/ethereum/go-ethereum/common" + chainsel "github.com/smartcontractkit/chain-selectors" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/deployment" + common2 "github.com/smartcontractkit/chainlink/deployment/common" + "github.com/smartcontractkit/chainlink/deployment/environment/memory" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestDeployMCMSWithConfig(t *testing.T) { + lggr := logger.TestLogger(t) + chains := memory.NewMemoryChainsWithChainIDs(t, []uint64{ + chainsel.TEST_90000001.EvmChainID, + }) + ab := deployment.NewMemoryAddressBook() + _, err := DeployMCMSWithConfig(ProposerManyChainMultisig, + lggr, chains[chainsel.TEST_90000001.Selector], ab, common2.SingleGroupMCMS(t)) + require.NoError(t, err) +} + +func TestDeployMCMSWithTimelockContracts(t *testing.T) { + lggr := logger.TestLogger(t) + chains := memory.NewMemoryChainsWithChainIDs(t, []uint64{ + chainsel.TEST_90000001.EvmChainID, + }) + ab := deployment.NewMemoryAddressBook() + _, err := DeployMCMSWithTimelockContracts(lggr, + chains[chainsel.TEST_90000001.Selector], + ab, MCMSConfig{ + Canceller: common2.SingleGroupMCMS(t), + Bypasser: common2.SingleGroupMCMS(t), + Proposer: common2.SingleGroupMCMS(t), + Executors: []common.Address{ + chains[chainsel.TEST_90000001.Selector].DeployerKey.From, + }, + }) + require.NoError(t, err) + addresses, err := ab.AddressesForChain(chainsel.TEST_90000001.Selector) + require.NoError(t, err) + require.Len(t, addresses, 4) + mcmsState, err := common2.LoadMCMSWithTimelockState(chains[chainsel.TEST_90000001.Selector], addresses) + require.NoError(t, err) + v, err := mcmsState.GenerateMCMSWithTimelockView() + b, err := json.MarshalIndent(v, "", " ") + require.NoError(t, err) + t.Log(string(b)) +} diff --git a/deployment/common/changeset/mcms_test_helpers.go b/deployment/common/changeset/mcms_test_helpers.go new file mode 100644 index 00000000000..3951149815c --- /dev/null +++ b/deployment/common/changeset/mcms_test_helpers.go @@ -0,0 +1,115 @@ +package changeset + +import ( + "bytes" + "context" + "crypto/ecdsa" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" + owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" + chainsel "github.com/smartcontractkit/chain-selectors" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/deployment" +) + +var ( + // TestXXXMCMSSigner is a throwaway private key used for signing MCMS proposals. + // in tests. + TestXXXMCMSSigner *ecdsa.PrivateKey +) + +func init() { + key, err := crypto.GenerateKey() + if err != nil { + panic(err) + } + TestXXXMCMSSigner = key +} + +func SingleGroupMCMS(t *testing.T) config.Config { + publicKey := TestXXXMCMSSigner.Public().(*ecdsa.PublicKey) + // Convert the public key to an Ethereum address + address := crypto.PubkeyToAddress(*publicKey) + c, err := config.NewConfig(1, []common.Address{address}, []config.Config{}) + require.NoError(t, err) + return *c +} + +func SignProposal(t *testing.T, env deployment.Environment, proposal *timelock.MCMSWithTimelockProposal) *mcms.Executor { + executorClients := make(map[mcms.ChainIdentifier]mcms.ContractDeployBackend) + for _, chain := range env.Chains { + chainselc, exists := chainsel.ChainBySelector(chain.Selector) + require.True(t, exists) + chainSel := mcms.ChainIdentifier(chainselc.Selector) + executorClients[chainSel] = chain.Client + } + executor, err := proposal.ToExecutor(true) + require.NoError(t, err) + payload, err := executor.SigningHash() + require.NoError(t, err) + // Sign the payload + sig, err := crypto.Sign(payload.Bytes(), TestXXXMCMSSigner) + require.NoError(t, err) + mcmSig, err := mcms.NewSignatureFromBytes(sig) + require.NoError(t, err) + executor.Proposal.AddSignature(mcmSig) + require.NoError(t, executor.Proposal.Validate()) + return executor +} + +func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Executor, + timelock *owner_helpers.RBACTimelock, sel uint64) { + 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)) + if err2 != nil { + require.NoError(t, deployment.MaybeDataErr(err2)) + } + _, err2 = env.Chains[sel].Confirm(tx) + require.NoError(t, err2) + + // TODO: This sort of helper probably should move to the MCMS lib. + // Execute all the transactions in the proposal which are for this chain. + for _, chainOp := range executor.Operations[mcms.ChainIdentifier(sel)] { + for idx, op := range executor.ChainAgnosticOps { + if bytes.Equal(op.Data, chainOp.Data) && op.To == chainOp.To { + opTx, err3 := executor.ExecuteOnChain(env.Chains[sel].Client, env.Chains[sel].DeployerKey, idx) + require.NoError(t, err3) + block, err3 := env.Chains[sel].Confirm(opTx) + require.NoError(t, err3) + t.Log("executed", chainOp) + it, err3 := timelock.FilterCallScheduled(&bind.FilterOpts{ + Start: block, + End: &block, + Context: context.Background(), + }, nil, nil) + require.NoError(t, err3) + var calls []owner_helpers.RBACTimelockCall + var pred, salt [32]byte + for it.Next() { + // Note these are the same for the whole batch, can overwrite + pred = it.Event.Predecessor + salt = it.Event.Salt + t.Log("scheduled", it.Event) + calls = append(calls, owner_helpers.RBACTimelockCall{ + Target: it.Event.Target, + Data: it.Event.Data, + Value: it.Event.Value, + }) + } + tx, err := timelock.ExecuteBatch( + env.Chains[sel].DeployerKey, calls, pred, salt) + require.NoError(t, err) + _, err = env.Chains[sel].Confirm(tx) + require.NoError(t, err) + } + } + } +} diff --git a/deployment/common/changeset/state.go b/deployment/common/changeset/state.go new file mode 100644 index 00000000000..b2dcf656aaa --- /dev/null +++ b/deployment/common/changeset/state.go @@ -0,0 +1,108 @@ +package changeset + +import ( + "errors" + + "github.com/ethereum/go-ethereum/common" + owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" +) + +const ( + BypasserManyChainMultisig deployment.ContractType = "BypasserManyChainMultiSig" + CancellerManyChainMultisig deployment.ContractType = "CancellerManyChainMultiSig" + ProposerManyChainMultisig deployment.ContractType = "ProposerManyChainMultiSig" + RBACTimelock deployment.ContractType = "RBACTimelock" +) + +// MCMSWithTimelockState holds the Go bindings +// for a MCMSWithTimelock contract deployment. +// It is public for use in product specific packages. +type MCMSWithTimelockState struct { + CancellerMcm *owner_helpers.ManyChainMultiSig + BypasserMcm *owner_helpers.ManyChainMultiSig + ProposerMcm *owner_helpers.ManyChainMultiSig + Timelock *owner_helpers.RBACTimelock +} + +func (state MCMSWithTimelockState) Validate() error { + if state.Timelock == nil { + return errors.New("timelock not found") + } + if state.CancellerMcm == nil { + return errors.New("canceller not found") + } + if state.ProposerMcm == nil { + return errors.New("proposer not found") + } + if state.BypasserMcm == nil { + return errors.New("bypasser not found") + } + return nil +} + +func (state MCMSWithTimelockState) GenerateMCMSWithTimelockView() (v1_0.MCMSWithTimelockView, error) { + if err := state.Validate(); err != nil { + return v1_0.MCMSWithTimelockView{}, err + } + timelockView, err := v1_0.GenerateTimelockView(*state.Timelock) + if err != nil { + return v1_0.MCMSWithTimelockView{}, nil + } + bypasserView, err := v1_0.GenerateMCMSView(*state.BypasserMcm) + if err != nil { + return v1_0.MCMSWithTimelockView{}, nil + } + proposerView, err := v1_0.GenerateMCMSView(*state.ProposerMcm) + if err != nil { + return v1_0.MCMSWithTimelockView{}, nil + } + cancellerView, err := v1_0.GenerateMCMSView(*state.CancellerMcm) + if err != nil { + return v1_0.MCMSWithTimelockView{}, nil + } + return v1_0.MCMSWithTimelockView{ + Timelock: timelockView, + Bypasser: bypasserView, + Proposer: proposerView, + Canceller: cancellerView, + }, nil +} + +func LoadMCMSWithTimelockState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (*MCMSWithTimelockState, error) { + state := MCMSWithTimelockState{} + for address, tvStr := range addresses { + switch tvStr.String() { + case deployment.NewTypeAndVersion(RBACTimelock, deployment.Version1_0_0).String(): + tl, err := owner_helpers.NewRBACTimelock(common.HexToAddress(address), chain.Client) + if err != nil { + return nil, err + } + state.Timelock = tl + case deployment.NewTypeAndVersion(ProposerManyChainMultisig, deployment.Version1_0_0).String(): + mcms, err := owner_helpers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) + if err != nil { + return nil, err + } + state.ProposerMcm = mcms + case deployment.NewTypeAndVersion(BypasserManyChainMultisig, deployment.Version1_0_0).String(): + mcms, err := owner_helpers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) + if err != nil { + return nil, err + } + state.BypasserMcm = mcms + case deployment.NewTypeAndVersion(CancellerManyChainMultisig, deployment.Version1_0_0).String(): + mcms, err := owner_helpers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) + if err != nil { + return nil, err + } + state.CancellerMcm = mcms + } + } + if err := state.Validate(); err != nil { + return nil, errors.New("missing contract") + } + return &state, nil +} diff --git a/deployment/common/view/v1_0/mcms.go b/deployment/common/view/v1_0/mcms.go new file mode 100644 index 00000000000..25ca614a553 --- /dev/null +++ b/deployment/common/view/v1_0/mcms.go @@ -0,0 +1,146 @@ +package v1_0 + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" + owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" + + "github.com/smartcontractkit/chainlink/deployment/common/view/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" +) + +type Role struct { + ID common.Hash + Name string +} + +const ( + EXECUTOR_ROLE_STR = "EXECUTOR_ROLE" + BYPASSER_ROLE_STR = "BYPASSER_ROLE" + CANCELLER_ROLE_STR = "CANCELLER_ROLE" + PROPOSER_ROLE_STR = "PROPOSER_ROLE" + ADMIN_ROLE_STR = "ADMIN_ROLE" +) + +// https://github.com/smartcontractkit/ccip-owner-contracts/blob/9d81692b324ce7ea2ef8a75e683889edbc7e2dd0/src/RBACTimelock.sol#L71 +// Just to avoid invoking the Go binding to get these. +var ( + ADMIN_ROLE = Role{ + ID: utils.MustHash(ADMIN_ROLE_STR), + Name: ADMIN_ROLE_STR, + } + PROPOSER_ROLE = Role{ + ID: utils.MustHash(PROPOSER_ROLE_STR), + Name: PROPOSER_ROLE_STR, + } + BYPASSER_ROLE = Role{ + ID: utils.MustHash(BYPASSER_ROLE_STR), + Name: BYPASSER_ROLE_STR, + } + CANCELLER_ROLE = Role{ + ID: utils.MustHash(CANCELLER_ROLE_STR), + Name: CANCELLER_ROLE_STR, + } + EXECUTOR_ROLE = Role{ + ID: utils.MustHash(EXECUTOR_ROLE_STR), + Name: EXECUTOR_ROLE_STR, + } +) + +type MCMSView struct { + types.ContractMetaData + // Note config is json marshallable. + Config config.Config `json:"config"` +} + +func GenerateMCMSView(mcms owner_helpers.ManyChainMultiSig) (MCMSView, error) { + owner, err := mcms.Owner(nil) + if err != nil { + return MCMSView{}, nil + } + c, err := mcms.GetConfig(nil) + if err != nil { + return MCMSView{}, nil + } + parsedConfig, err := config.NewConfigFromRaw(c) + if err != nil { + return MCMSView{}, nil + } + return MCMSView{ + // Has no type and version on the contract + ContractMetaData: types.ContractMetaData{ + Owner: owner, + Address: mcms.Address(), + }, + Config: *parsedConfig, + }, nil +} + +type TimelockView struct { + types.ContractMetaData + MembersByRole map[string][]common.Address `json:"membersByRole"` +} + +func GenerateTimelockView(tl owner_helpers.RBACTimelock) (TimelockView, error) { + membersByRole := make(map[string][]common.Address) + for _, role := range []Role{ADMIN_ROLE, PROPOSER_ROLE, BYPASSER_ROLE, CANCELLER_ROLE, EXECUTOR_ROLE} { + numMembers, err := tl.GetRoleMemberCount(nil, role.ID) + if err != nil { + return TimelockView{}, nil + } + for i := int64(0); i < numMembers.Int64(); i++ { + member, err2 := tl.GetRoleMember(nil, role.ID, big.NewInt(i)) + if err2 != nil { + return TimelockView{}, nil + } + membersByRole[role.Name] = append(membersByRole[role.Name], member) + } + } + return TimelockView{ + // Has no type and version or owner. + ContractMetaData: types.ContractMetaData{ + Address: tl.Address(), + }, + MembersByRole: membersByRole, + }, nil +} + +type MCMSWithTimelockView struct { + Bypasser MCMSView `json:"bypasser"` + Canceller MCMSView `json:"canceller"` + Proposer MCMSView `json:"proposer"` + Timelock TimelockView `json:"timelock"` +} + +func GenerateMCMSWithTimelockView( + bypasser owner_helpers.ManyChainMultiSig, + canceller owner_helpers.ManyChainMultiSig, + proposer owner_helpers.ManyChainMultiSig, + timelock owner_helpers.RBACTimelock, +) (MCMSWithTimelockView, error) { + timelockView, err := GenerateTimelockView(timelock) + if err != nil { + return MCMSWithTimelockView{}, nil + } + bypasserView, err := GenerateMCMSView(bypasser) + if err != nil { + return MCMSWithTimelockView{}, nil + } + proposerView, err := GenerateMCMSView(proposer) + if err != nil { + return MCMSWithTimelockView{}, nil + } + cancellerView, err := GenerateMCMSView(canceller) + if err != nil { + return MCMSWithTimelockView{}, nil + } + + return MCMSWithTimelockView{ + Timelock: timelockView, + Bypasser: bypasserView, + Proposer: proposerView, + Canceller: cancellerView, + }, nil +} From 29843d8fa647322d12f0bd03e0b5a835b781446a Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 18 Nov 2024 11:39:46 -0500 Subject: [PATCH 02/13] Use changeset --- deployment/ccip/changeset/add_chain_test.go | 23 ++++++++++++++++- deployment/ccip/state.go | 7 +++++- deployment/ccip/test_helpers.go | 5 ++-- .../changeset/deploy_mcms_with_timelock.go | 18 +++---------- deployment/common/changeset/internal/mcms.go | 11 ++++---- deployment/common/changeset/state.go | 19 ++++---------- deployment/common/types/types.go | 25 +++++++++++++++++++ deployment/environment.go | 8 ++++++ .../smoke/ccip_messaging_test.go | 21 +++++++++++++--- integration-tests/smoke/ccip_test.go | 21 ++++++++++++---- 10 files changed, 111 insertions(+), 47 deletions(-) create mode 100644 deployment/common/types/types.go diff --git a/deployment/ccip/changeset/add_chain_test.go b/deployment/ccip/changeset/add_chain_test.go index 69f66acb730..2393c1b2a68 100644 --- a/deployment/ccip/changeset/add_chain_test.go +++ b/deployment/ccip/changeset/add_chain_test.go @@ -1,12 +1,13 @@ package changeset import ( + "math/big" "testing" "time" ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" commondeployment "github.com/smartcontractkit/chainlink/deployment/common/changeset" - + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" "github.com/ethereum/go-ethereum/common" @@ -39,6 +40,20 @@ func TestAddChainInbound(t *testing.T) { initialDeploy := e.Env.AllChainSelectorsExcluding([]uint64{newChain}) tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) + cfg := commontypes.MCMSWithTimelockConfig{ + Canceller: commondeployment.SingleGroupMCMS(t), + Bypasser: commondeployment.SingleGroupMCMS(t), + Proposer: commondeployment.SingleGroupMCMS(t), + TimelockExecutors: e.Env.AllDeployerKeys(), + TimelockMinDelay: big.NewInt(0), + } + out, err := commondeployment.DeployMCMSWithTimelock(e.Env, map[uint64]commontypes.MCMSWithTimelockConfig{ + initialDeploy[0]: cfg, + initialDeploy[1]: cfg, + initialDeploy[2]: cfg, + }) + require.NoError(t, err) + require.NoError(t, e.Env.ExistingAddresses.Merge(out.AddressBook)) newAddresses := deployment.NewMemoryAddressBook() err = ccipdeployment.DeployCCIPContracts(e.Env, newAddresses, ccipdeployment.DeployCCIPContractConfig{ HomeChainSel: e.HomeChainSel, @@ -68,6 +83,12 @@ func TestAddChainInbound(t *testing.T) { require.NoError(t, err) // Deploy contracts to new chain + out, err = commondeployment.DeployMCMSWithTimelock(e.Env, map[uint64]commontypes.MCMSWithTimelockConfig{ + newChain: cfg, + }) + require.NoError(t, err) + require.NoError(t, e.Env.ExistingAddresses.Merge(out.AddressBook)) + newChainAddresses := deployment.NewMemoryAddressBook() err = ccipdeployment.DeployChainContracts(e.Env, e.Env.Chains[newChain], newChainAddresses, diff --git a/deployment/ccip/state.go b/deployment/ccip/state.go index 271eaa716f0..bcc56894700 100644 --- a/deployment/ccip/state.go +++ b/deployment/ccip/state.go @@ -15,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_5" "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_6" commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" common_v1_0 "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" @@ -228,7 +229,6 @@ func LoadOnchainState(e deployment.Environment) (CCIPOnChainState, error) { } // LoadChainState Loads all state for a chain into state -// Modifies map in place func LoadChainState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (CCIPChainState, error) { var state CCIPChainState mcmsWithTimelock, err := commoncs.LoadMCMSWithTimelockState(chain, addresses) @@ -238,6 +238,11 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type state.MCMSWithTimelockState = *mcmsWithTimelock for address, tvStr := range addresses { switch tvStr.String() { + case deployment.NewTypeAndVersion(commontypes.RBACTimelock, deployment.Version1_0_0).String(), + deployment.NewTypeAndVersion(commontypes.ProposerManyChainMultisig, deployment.Version1_0_0).String(), + deployment.NewTypeAndVersion(commontypes.CancellerManyChainMultisig, deployment.Version1_0_0).String(), + deployment.NewTypeAndVersion(commontypes.BypasserManyChainMultisig, deployment.Version1_0_0).String(): + continue case deployment.NewTypeAndVersion(CapabilitiesRegistry, deployment.Version1_0_0).String(): cr, err := capabilities_registry.NewCapabilitiesRegistry(common.HexToAddress(address), chain.Client) if err != nil { diff --git a/deployment/ccip/test_helpers.go b/deployment/ccip/test_helpers.go index 70be57b2f38..6b3a9426778 100644 --- a/deployment/ccip/test_helpers.go +++ b/deployment/ccip/test_helpers.go @@ -19,6 +19,7 @@ import ( 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" + commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" @@ -489,9 +490,9 @@ func ProcessChangeset(t *testing.T, e deployment.Environment, c deployment.Chang chains.Add(uint64(op.ChainIdentifier)) } - signed := SignProposal(t, e, &prop) + signed := commoncs.SignProposal(t, e, &prop) for _, sel := range chains.ToSlice() { - ExecuteProposal(t, e, signed, state, sel) + commoncs.ExecuteProposal(t, e, signed, state.Chains[sel].Timelock, sel) } } } diff --git a/deployment/common/changeset/deploy_mcms_with_timelock.go b/deployment/common/changeset/deploy_mcms_with_timelock.go index 88caea1c125..686d3bae19b 100644 --- a/deployment/common/changeset/deploy_mcms_with_timelock.go +++ b/deployment/common/changeset/deploy_mcms_with_timelock.go @@ -1,26 +1,14 @@ package changeset import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" - "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal" + "github.com/smartcontractkit/chainlink/deployment/common/types" ) -var _ deployment.ChangeSet[map[uint64]MCMSWithTimelockConfig] = DeployMCMSWithTimelock - -type MCMSWithTimelockConfig struct { - Canceller config.Config - Bypasser config.Config - Proposer config.Config - TimelockExecutors []common.Address - TimelockMinDelay *big.Int -} +var _ deployment.ChangeSet[map[uint64]types.MCMSWithTimelockConfig] = DeployMCMSWithTimelock -func DeployMCMSWithTimelock(e deployment.Environment, cfgByChain map[uint64]MCMSWithTimelockConfig) (deployment.ChangesetOutput, error) { +func DeployMCMSWithTimelock(e deployment.Environment, cfgByChain map[uint64]types.MCMSWithTimelockConfig) (deployment.ChangesetOutput, error) { newAddresses := deployment.NewMemoryAddressBook() err := internal.DeployMCMSWithTimelockContractsBatch( e.Logger, e.Chains, newAddresses, cfgByChain, diff --git a/deployment/common/changeset/internal/mcms.go b/deployment/common/changeset/internal/mcms.go index cf7ef95e629..1e2fb958aae 100644 --- a/deployment/common/changeset/internal/mcms.go +++ b/deployment/common/changeset/internal/mcms.go @@ -7,7 +7,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" ) @@ -61,7 +60,7 @@ func DeployMCMSWithTimelockContractsBatch( lggr logger.Logger, chains map[uint64]deployment.Chain, ab deployment.AddressBook, - cfgByChain map[uint64]changeset.MCMSWithTimelockConfig, + cfgByChain map[uint64]types.MCMSWithTimelockConfig, ) error { for chainSel, cfg := range cfgByChain { _, err := DeployMCMSWithTimelockContracts(lggr, chains[chainSel], ab, cfg) @@ -83,15 +82,15 @@ func DeployMCMSWithTimelockContracts( ab deployment.AddressBook, config types.MCMSWithTimelockConfig, ) (*MCMSWithTimelockDeploy, error) { - bypasser, err := DeployMCMSWithConfig(BypasserManyChainMultisig, lggr, chain, ab, config.Bypasser) + bypasser, err := DeployMCMSWithConfig(types.BypasserManyChainMultisig, lggr, chain, ab, config.Bypasser) if err != nil { return nil, err } - canceller, err := DeployMCMSWithConfig(CancellerManyChainMultisig, lggr, chain, ab, config.Canceller) + canceller, err := DeployMCMSWithConfig(types.CancellerManyChainMultisig, lggr, chain, ab, config.Canceller) if err != nil { return nil, err } - proposer, err := DeployMCMSWithConfig(ProposerManyChainMultisig, lggr, chain, ab, config.Proposer) + proposer, err := DeployMCMSWithConfig(types.ProposerManyChainMultisig, lggr, chain, ab, config.Proposer) if err != nil { return nil, err } @@ -112,7 +111,7 @@ func DeployMCMSWithTimelockContracts( []common.Address{bypasser.Address}, // bypassers ) return deployment.ContractDeploy[*owner_helpers.RBACTimelock]{ - timelock, cc, tx2, deployment.NewTypeAndVersion(RBACTimelock, deployment.Version1_0_0), err2, + timelock, cc, tx2, deployment.NewTypeAndVersion(types.RBACTimelock, deployment.Version1_0_0), err2, } }) if err != nil { diff --git a/deployment/common/changeset/state.go b/deployment/common/changeset/state.go index b2dcf656aaa..38a1d02c044 100644 --- a/deployment/common/changeset/state.go +++ b/deployment/common/changeset/state.go @@ -7,16 +7,10 @@ import ( owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" ) -const ( - BypasserManyChainMultisig deployment.ContractType = "BypasserManyChainMultiSig" - CancellerManyChainMultisig deployment.ContractType = "CancellerManyChainMultiSig" - ProposerManyChainMultisig deployment.ContractType = "ProposerManyChainMultiSig" - RBACTimelock deployment.ContractType = "RBACTimelock" -) - // MCMSWithTimelockState holds the Go bindings // for a MCMSWithTimelock contract deployment. // It is public for use in product specific packages. @@ -75,25 +69,25 @@ func LoadMCMSWithTimelockState(chain deployment.Chain, addresses map[string]depl state := MCMSWithTimelockState{} for address, tvStr := range addresses { switch tvStr.String() { - case deployment.NewTypeAndVersion(RBACTimelock, deployment.Version1_0_0).String(): + case deployment.NewTypeAndVersion(types.RBACTimelock, deployment.Version1_0_0).String(): tl, err := owner_helpers.NewRBACTimelock(common.HexToAddress(address), chain.Client) if err != nil { return nil, err } state.Timelock = tl - case deployment.NewTypeAndVersion(ProposerManyChainMultisig, deployment.Version1_0_0).String(): + case deployment.NewTypeAndVersion(types.ProposerManyChainMultisig, deployment.Version1_0_0).String(): mcms, err := owner_helpers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) if err != nil { return nil, err } state.ProposerMcm = mcms - case deployment.NewTypeAndVersion(BypasserManyChainMultisig, deployment.Version1_0_0).String(): + case deployment.NewTypeAndVersion(types.BypasserManyChainMultisig, deployment.Version1_0_0).String(): mcms, err := owner_helpers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) if err != nil { return nil, err } state.BypasserMcm = mcms - case deployment.NewTypeAndVersion(CancellerManyChainMultisig, deployment.Version1_0_0).String(): + case deployment.NewTypeAndVersion(types.CancellerManyChainMultisig, deployment.Version1_0_0).String(): mcms, err := owner_helpers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) if err != nil { return nil, err @@ -101,8 +95,5 @@ func LoadMCMSWithTimelockState(chain deployment.Chain, addresses map[string]depl state.CancellerMcm = mcms } } - if err := state.Validate(); err != nil { - return nil, errors.New("missing contract") - } return &state, nil } diff --git a/deployment/common/types/types.go b/deployment/common/types/types.go new file mode 100644 index 00000000000..0efb226d73b --- /dev/null +++ b/deployment/common/types/types.go @@ -0,0 +1,25 @@ +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" + + "github.com/smartcontractkit/chainlink/deployment" +) + +const ( + BypasserManyChainMultisig deployment.ContractType = "BypasserManyChainMultiSig" + CancellerManyChainMultisig deployment.ContractType = "CancellerManyChainMultiSig" + ProposerManyChainMultisig deployment.ContractType = "ProposerManyChainMultiSig" + RBACTimelock deployment.ContractType = "RBACTimelock" +) + +type MCMSWithTimelockConfig struct { + Canceller config.Config + Bypasser config.Config + Proposer config.Config + TimelockExecutors []common.Address + TimelockMinDelay *big.Int +} diff --git a/deployment/environment.go b/deployment/environment.go index 104db4c5c37..2fd1876afd8 100644 --- a/deployment/environment.go +++ b/deployment/environment.go @@ -126,6 +126,14 @@ func (e Environment) AllChainSelectorsExcluding(excluding []uint64) []uint64 { return selectors } +func (e Environment) AllDeployerKeys() []common.Address { + var deployerKeys []common.Address + for sel := range e.Chains { + deployerKeys = append(deployerKeys, e.Chains[sel].DeployerKey.From) + } + return deployerKeys +} + func ConfirmIfNoError(chain Chain, tx *types.Transaction, err error) (uint64, error) { if err != nil { //revive:disable diff --git a/integration-tests/smoke/ccip_messaging_test.go b/integration-tests/smoke/ccip_messaging_test.go index 55309598c8c..f384f51a726 100644 --- a/integration-tests/smoke/ccip_messaging_test.go +++ b/integration-tests/smoke/ccip_messaging_test.go @@ -1,6 +1,7 @@ package smoke import ( + "math/big" "testing" "time" @@ -15,6 +16,8 @@ import ( ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + commondeployment "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "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" @@ -60,13 +63,25 @@ func Test_CCIPMessaging(t *testing.T) { ) tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) - // Apply migration - output, err := changeset.InitialDeploy(e.Env, ccdeploy.DeployCCIPContractConfig{ + + cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) + for _, chain := range allChainSelectors { + cfg[chain] = commontypes.MCMSWithTimelockConfig{ + Canceller: commondeployment.SingleGroupMCMS(t), + Bypasser: commondeployment.SingleGroupMCMS(t), + Proposer: commondeployment.SingleGroupMCMS(t), + TimelockExecutors: e.Env.AllDeployerKeys(), + TimelockMinDelay: big.NewInt(0), + } + } + output, err := commondeployment.DeployMCMSWithTimelock(e.Env, cfg) + require.NoError(t, err) + require.NoError(t, e.Env.ExistingAddresses.Merge(output.AddressBook)) + output, err = changeset.InitialDeploy(e.Env, ccdeploy.DeployCCIPContractConfig{ HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, ChainsToDeploy: allChainSelectors, TokenConfig: tokenConfig, - MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e.Env), OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), }) require.NoError(t, err) diff --git a/integration-tests/smoke/ccip_test.go b/integration-tests/smoke/ccip_test.go index 5b0ba285527..745f4a1816f 100644 --- a/integration-tests/smoke/ccip_test.go +++ b/integration-tests/smoke/ccip_test.go @@ -11,6 +11,8 @@ import ( "github.com/smartcontractkit/chainlink-ccip/pluginconfig" jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + commondeployment "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" @@ -38,13 +40,11 @@ func TestInitialDeployOnLocal(t *testing.T) { DeviationPPB: cciptypes.NewBigIntFromInt64(1e9), }, ) - // Apply migration output, err := changeset.InitialDeploy(tenv.Env, ccdeploy.DeployCCIPContractConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), TokenConfig: tokenConfig, - MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e), OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), }) require.NoError(t, err) @@ -132,13 +132,24 @@ func TestTokenTransfer(t *testing.T) { }, ) - // Apply migration - output, err := changeset.InitialDeploy(e, ccdeploy.DeployCCIPContractConfig{ + cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) + for _, chain := range e.AllChainSelectors() { + cfg[chain] = commontypes.MCMSWithTimelockConfig{ + Canceller: commondeployment.SingleGroupMCMS(t), + Bypasser: commondeployment.SingleGroupMCMS(t), + Proposer: commondeployment.SingleGroupMCMS(t), + TimelockExecutors: e.AllDeployerKeys(), + TimelockMinDelay: big.NewInt(0), + } + } + output, err := commondeployment.DeployMCMSWithTimelock(e, cfg) + require.NoError(t, err) + require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) + output, err = changeset.InitialDeploy(e, ccdeploy.DeployCCIPContractConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: e.AllChainSelectors(), TokenConfig: tokenConfig, - MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e), OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), }) require.NoError(t, err) From 190a457e8987f22c2e8f808fde344d89e94a2e21 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 18 Nov 2024 11:57:29 -0500 Subject: [PATCH 03/13] Standardize package import names --- .../ccip/changeset/active_candidate_test.go | 18 +++++++++--------- deployment/ccip/changeset/add_chain_test.go | 16 ++++++++-------- .../ccip/changeset/initial_deploy_test.go | 10 +++++----- integration-tests/smoke/ccip_messaging_test.go | 10 +++++----- integration-tests/smoke/ccip_test.go | 18 +++++++++--------- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/deployment/ccip/changeset/active_candidate_test.go b/deployment/ccip/changeset/active_candidate_test.go index e0f1c42c4d9..eab651ff9c9 100644 --- a/deployment/ccip/changeset/active_candidate_test.go +++ b/deployment/ccip/changeset/active_candidate_test.go @@ -19,7 +19,7 @@ import ( jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" - commondeploy "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -114,9 +114,9 @@ func TestActiveCandidate(t *testing.T) { ccdeploy.TransferAllOwnership(t, state, homeCS, e) acceptOwnershipProposal, err := ccdeploy.GenerateAcceptOwnershipProposal(state, homeCS, e.AllChainSelectors()) require.NoError(t, err) - acceptOwnershipExec := commondeploy.SignProposal(t, e, acceptOwnershipProposal) + acceptOwnershipExec := commonchangeset.SignProposal(t, e, acceptOwnershipProposal) for _, sel := range e.AllChainSelectors() { - commondeploy.ExecuteProposal(t, e, acceptOwnershipExec, state.Chains[sel].Timelock, sel) + commonchangeset.ExecuteProposal(t, e, acceptOwnershipExec, state.Chains[sel].Timelock, sel) } // Apply the accept ownership proposal to all the chains. @@ -176,8 +176,8 @@ func TestActiveCandidate(t *testing.T) { Batch: setCommitCandidateOp, }}, "set new candidates on commit plugin", 0) require.NoError(t, err) - setCommitCandidateSigned := commondeploy.SignProposal(t, e, setCommitCandidateProposal) - commondeploy.ExecuteProposal(t, e, setCommitCandidateSigned, state.Chains[homeCS].Timelock, homeCS) + setCommitCandidateSigned := commonchangeset.SignProposal(t, e, setCommitCandidateProposal) + commonchangeset.ExecuteProposal(t, e, setCommitCandidateSigned, state.Chains[homeCS].Timelock, homeCS) // create the op for the commit plugin as well setExecCandidateOp, err := ccdeploy.SetCandidateOnExistingDon( @@ -194,8 +194,8 @@ func TestActiveCandidate(t *testing.T) { Batch: setExecCandidateOp, }}, "set new candidates on commit and exec plugins", 0) require.NoError(t, err) - setExecCandidateSigned := commondeploy.SignProposal(t, e, setExecCandidateProposal) - commondeploy.ExecuteProposal(t, e, setExecCandidateSigned, state.Chains[homeCS].Timelock, homeCS) + setExecCandidateSigned := commonchangeset.SignProposal(t, e, setExecCandidateProposal) + commonchangeset.ExecuteProposal(t, e, setExecCandidateSigned, state.Chains[homeCS].Timelock, homeCS) // check setup was successful by confirming number of nodes from cap reg donInfo, err = state.Chains[homeCS].CapabilityRegistry.GetDON(nil, donID) @@ -221,8 +221,8 @@ func TestActiveCandidate(t *testing.T) { Batch: promoteOps, }}, "promote candidates and revoke actives", 0) require.NoError(t, err) - promoteSigned := commondeploy.SignProposal(t, e, promoteProposal) - commondeploy.ExecuteProposal(t, e, promoteSigned, state.Chains[homeCS].Timelock, homeCS) + promoteSigned := commonchangeset.SignProposal(t, e, promoteProposal) + commonchangeset.ExecuteProposal(t, e, promoteSigned, state.Chains[homeCS].Timelock, homeCS) // [NEW ACTIVE, NO CANDIDATE] done promoting // [NEW ACTIVE, NO CANDIDATE] check onchain state diff --git a/deployment/ccip/changeset/add_chain_test.go b/deployment/ccip/changeset/add_chain_test.go index 7844fea8474..76104871784 100644 --- a/deployment/ccip/changeset/add_chain_test.go +++ b/deployment/ccip/changeset/add_chain_test.go @@ -6,7 +6,7 @@ import ( "time" ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" - commondeployment "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" @@ -44,13 +44,13 @@ func TestAddChainInbound(t *testing.T) { require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) cfg := commontypes.MCMSWithTimelockConfig{ - Canceller: commondeployment.SingleGroupMCMS(t), - Bypasser: commondeployment.SingleGroupMCMS(t), - Proposer: commondeployment.SingleGroupMCMS(t), + Canceller: commonchangeset.SingleGroupMCMS(t), + Bypasser: commonchangeset.SingleGroupMCMS(t), + Proposer: commonchangeset.SingleGroupMCMS(t), TimelockExecutors: e.Env.AllDeployerKeys(), TimelockMinDelay: big.NewInt(0), } - out, err := commondeployment.DeployMCMSWithTimelock(e.Env, map[uint64]commontypes.MCMSWithTimelockConfig{ + out, err := commonchangeset.DeployMCMSWithTimelock(e.Env, map[uint64]commontypes.MCMSWithTimelockConfig{ initialDeploy[0]: cfg, initialDeploy[1]: cfg, initialDeploy[2]: cfg, @@ -87,7 +87,7 @@ func TestAddChainInbound(t *testing.T) { require.NoError(t, err) // Deploy contracts to new chain - out, err = commondeployment.DeployMCMSWithTimelock(e.Env, map[uint64]commontypes.MCMSWithTimelockConfig{ + out, err = commonchangeset.DeployMCMSWithTimelock(e.Env, map[uint64]commontypes.MCMSWithTimelockConfig{ newChain: cfg, }) require.NoError(t, err) @@ -137,10 +137,10 @@ func TestAddChainInbound(t *testing.T) { acceptOwnershipProposal, err := ccipdeployment.GenerateAcceptOwnershipProposal(state, e.HomeChainSel, initialDeploy) require.NoError(t, err) - acceptOwnershipExec := commondeployment.SignProposal(t, e.Env, acceptOwnershipProposal) + acceptOwnershipExec := commonchangeset.SignProposal(t, e.Env, acceptOwnershipProposal) // Apply the accept ownership proposal to all the chains. for _, sel := range initialDeploy { - commondeployment.ExecuteProposal(t, e.Env, acceptOwnershipExec, state.Chains[sel].Timelock, sel) + commonchangeset.ExecuteProposal(t, e.Env, acceptOwnershipExec, state.Chains[sel].Timelock, sel) } for _, chain := range initialDeploy { owner, err2 := state.Chains[chain].OnRamp.Owner(nil) diff --git a/deployment/ccip/changeset/initial_deploy_test.go b/deployment/ccip/changeset/initial_deploy_test.go index 94abb9011b3..a299dd4971f 100644 --- a/deployment/ccip/changeset/initial_deploy_test.go +++ b/deployment/ccip/changeset/initial_deploy_test.go @@ -8,7 +8,7 @@ import ( jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" - commondeployment "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment" @@ -39,14 +39,14 @@ func TestInitialDeploy(t *testing.T) { cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) for _, chain := range e.AllChainSelectors() { cfg[chain] = commontypes.MCMSWithTimelockConfig{ - Canceller: commondeployment.SingleGroupMCMS(t), - Bypasser: commondeployment.SingleGroupMCMS(t), - Proposer: commondeployment.SingleGroupMCMS(t), + Canceller: commonchangeset.SingleGroupMCMS(t), + Bypasser: commonchangeset.SingleGroupMCMS(t), + Proposer: commonchangeset.SingleGroupMCMS(t), TimelockExecutors: e.AllDeployerKeys(), TimelockMinDelay: big.NewInt(0), } } - output, err = commondeployment.DeployMCMSWithTimelock(e, cfg) + output, err = commonchangeset.DeployMCMSWithTimelock(e, cfg) require.NoError(t, err) require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) diff --git a/integration-tests/smoke/ccip_messaging_test.go b/integration-tests/smoke/ccip_messaging_test.go index fa210bd7a46..cbe5f7b8ba1 100644 --- a/integration-tests/smoke/ccip_messaging_test.go +++ b/integration-tests/smoke/ccip_messaging_test.go @@ -20,7 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - commondeployment "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testsetups" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" @@ -77,14 +77,14 @@ func Test_CCIPMessaging(t *testing.T) { cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) for _, chain := range allChainSelectors { cfg[chain] = commontypes.MCMSWithTimelockConfig{ - Canceller: commondeployment.SingleGroupMCMS(t), - Bypasser: commondeployment.SingleGroupMCMS(t), - Proposer: commondeployment.SingleGroupMCMS(t), + Canceller: commonchangeset.SingleGroupMCMS(t), + Bypasser: commonchangeset.SingleGroupMCMS(t), + Proposer: commonchangeset.SingleGroupMCMS(t), TimelockExecutors: e.Env.AllDeployerKeys(), TimelockMinDelay: big.NewInt(0), } } - output, err = commondeployment.DeployMCMSWithTimelock(e.Env, cfg) + output, err = commonchangeset.DeployMCMSWithTimelock(e.Env, cfg) require.NoError(t, err) require.NoError(t, e.Env.ExistingAddresses.Merge(output.AddressBook)) tokenConfig := ccdeploy.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) diff --git a/integration-tests/smoke/ccip_test.go b/integration-tests/smoke/ccip_test.go index a61da5af66e..3e7092cc937 100644 --- a/integration-tests/smoke/ccip_test.go +++ b/integration-tests/smoke/ccip_test.go @@ -9,7 +9,7 @@ import ( jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" - commondeployment "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment" @@ -40,14 +40,14 @@ func TestInitialDeployOnLocal(t *testing.T) { cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) for _, chain := range e.AllChainSelectors() { cfg[chain] = commontypes.MCMSWithTimelockConfig{ - Canceller: commondeployment.SingleGroupMCMS(t), - Bypasser: commondeployment.SingleGroupMCMS(t), - Proposer: commondeployment.SingleGroupMCMS(t), + Canceller: commonchangeset.SingleGroupMCMS(t), + Bypasser: commonchangeset.SingleGroupMCMS(t), + Proposer: commonchangeset.SingleGroupMCMS(t), TimelockExecutors: e.AllDeployerKeys(), TimelockMinDelay: big.NewInt(0), } } - output, err = commondeployment.DeployMCMSWithTimelock(e, cfg) + output, err = commonchangeset.DeployMCMSWithTimelock(e, cfg) require.NoError(t, err) require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) @@ -143,14 +143,14 @@ func TestTokenTransfer(t *testing.T) { cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) for _, chain := range e.AllChainSelectors() { cfg[chain] = commontypes.MCMSWithTimelockConfig{ - Canceller: commondeployment.SingleGroupMCMS(t), - Bypasser: commondeployment.SingleGroupMCMS(t), - Proposer: commondeployment.SingleGroupMCMS(t), + Canceller: commonchangeset.SingleGroupMCMS(t), + Bypasser: commonchangeset.SingleGroupMCMS(t), + Proposer: commonchangeset.SingleGroupMCMS(t), TimelockExecutors: e.AllDeployerKeys(), TimelockMinDelay: big.NewInt(0), } } - output, err = commondeployment.DeployMCMSWithTimelock(e, cfg) + output, err = commonchangeset.DeployMCMSWithTimelock(e, cfg) require.NoError(t, err) require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) output, err = changeset.InitialDeploy(e, ccdeploy.DeployCCIPContractConfig{ From a5c2f47606f524de66cea2e9cf8df19851c631dc Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 18 Nov 2024 12:09:26 -0500 Subject: [PATCH 04/13] Fix mcms test imports --- .../common/changeset/internal/mcms_test.go | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/deployment/common/changeset/internal/mcms_test.go b/deployment/common/changeset/internal/mcms_test.go index d0245946863..9969a0e5bc9 100644 --- a/deployment/common/changeset/internal/mcms_test.go +++ b/deployment/common/changeset/internal/mcms_test.go @@ -1,7 +1,8 @@ -package internal +package internal_test import ( "encoding/json" + "math/big" "testing" "github.com/ethereum/go-ethereum/common" @@ -9,7 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/deployment" - common2 "github.com/smartcontractkit/chainlink/deployment/common" + "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal" + "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -20,8 +23,8 @@ func TestDeployMCMSWithConfig(t *testing.T) { chainsel.TEST_90000001.EvmChainID, }) ab := deployment.NewMemoryAddressBook() - _, err := DeployMCMSWithConfig(ProposerManyChainMultisig, - lggr, chains[chainsel.TEST_90000001.Selector], ab, common2.SingleGroupMCMS(t)) + _, err := internal.DeployMCMSWithConfig(types.ProposerManyChainMultisig, + lggr, chains[chainsel.TEST_90000001.Selector], ab, changeset.SingleGroupMCMS(t)) require.NoError(t, err) } @@ -31,21 +34,22 @@ func TestDeployMCMSWithTimelockContracts(t *testing.T) { chainsel.TEST_90000001.EvmChainID, }) ab := deployment.NewMemoryAddressBook() - _, err := DeployMCMSWithTimelockContracts(lggr, + _, err := internal.DeployMCMSWithTimelockContracts(lggr, chains[chainsel.TEST_90000001.Selector], - ab, MCMSConfig{ - Canceller: common2.SingleGroupMCMS(t), - Bypasser: common2.SingleGroupMCMS(t), - Proposer: common2.SingleGroupMCMS(t), - Executors: []common.Address{ + ab, types.MCMSWithTimelockConfig{ + Canceller: changeset.SingleGroupMCMS(t), + Bypasser: changeset.SingleGroupMCMS(t), + Proposer: changeset.SingleGroupMCMS(t), + TimelockExecutors: []common.Address{ chains[chainsel.TEST_90000001.Selector].DeployerKey.From, }, + TimelockMinDelay: big.NewInt(0), }) require.NoError(t, err) addresses, err := ab.AddressesForChain(chainsel.TEST_90000001.Selector) require.NoError(t, err) require.Len(t, addresses, 4) - mcmsState, err := common2.LoadMCMSWithTimelockState(chains[chainsel.TEST_90000001.Selector], addresses) + mcmsState, err := changeset.LoadMCMSWithTimelockState(chains[chainsel.TEST_90000001.Selector], addresses) require.NoError(t, err) v, err := mcmsState.GenerateMCMSWithTimelockView() b, err := json.MarshalIndent(v, "", " ") From 9ac59557a8dd1d94385b9ae42f8c2a834d747ae0 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 18 Nov 2024 12:56:16 -0500 Subject: [PATCH 05/13] Fix add lane --- deployment/ccip/add_lane_test.go | 1 - deployment/ccip/deploy.go | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/deployment/ccip/add_lane_test.go b/deployment/ccip/add_lane_test.go index 5edfdae1ab0..15036476647 100644 --- a/deployment/ccip/add_lane_test.go +++ b/deployment/ccip/add_lane_test.go @@ -43,7 +43,6 @@ func TestAddLane(t *testing.T) { HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, TokenConfig: tokenConfig, - MCMSConfig: NewTestMCMSConfig(t, e.Env), ChainsToDeploy: []uint64{chain1, chain2}, OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), }) diff --git a/deployment/ccip/deploy.go b/deployment/ccip/deploy.go index 27f2771e7f4..7897c8dbb68 100644 --- a/deployment/ccip/deploy.go +++ b/deployment/ccip/deploy.go @@ -277,7 +277,10 @@ type DeployCCIPContractConfig struct { // It then deploys the rest of the CCIP chain contracts to the selected chains // registers the nodes with the capability registry and creates a DON for // each new chain. TODO: Might be better to break this down a bit? -func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c DeployCCIPContractConfig) error { +func DeployCCIPContracts( + e deployment.Environment, + ab deployment.AddressBook, + c DeployCCIPContractConfig) error { if c.OCRSecrets.IsEmpty() { return fmt.Errorf("OCR secrets are empty") } @@ -401,6 +404,9 @@ func DeployChainContracts( if chainState.Weth9 == nil { return fmt.Errorf("weth9 not found for chain %d, deploy the prerequisites first", chain.Selector) } + if chainState.Timelock == nil { + return fmt.Errorf("timelock not found for chain %d, deploy the mcms contracts first", chain.Selector) + } weth9Contract := chainState.Weth9 if chainState.LinkToken == nil { return fmt.Errorf("link token not found for chain %d, deploy the prerequisites first", chain.Selector) From cd981b824fc6f58d4316eed60e1407ff94a95b46 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 18 Nov 2024 14:52:35 -0500 Subject: [PATCH 06/13] Fix bad merge --- .../changeset/active_candidate_helpers.go | 139 --- deployment/ccip/changeset/add_lane.go | 138 --- deployment/ccip/changeset/add_lane_test.go | 154 --- deployment/ccip/changeset/consts.go | 11 - .../ccip/changeset/deploy_home_chain.go | 1085 ----------------- deployment/ccip/changeset/jobs.go | 57 - 6 files changed, 1584 deletions(-) delete mode 100644 deployment/ccip/changeset/active_candidate_helpers.go delete mode 100644 deployment/ccip/changeset/add_lane.go delete mode 100644 deployment/ccip/changeset/add_lane_test.go delete mode 100644 deployment/ccip/changeset/consts.go delete mode 100644 deployment/ccip/changeset/deploy_home_chain.go delete mode 100644 deployment/ccip/changeset/jobs.go diff --git a/deployment/ccip/changeset/active_candidate_helpers.go b/deployment/ccip/changeset/active_candidate_helpers.go deleted file mode 100644 index c65dac04103..00000000000 --- a/deployment/ccip/changeset/active_candidate_helpers.go +++ /dev/null @@ -1,139 +0,0 @@ -package ccipdeployment - -import ( - "fmt" - "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" - "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/keystone/generated/capabilities_registry" - "math/big" -) - -// SetCandidateExecPluginOps calls setCandidate on CCIPHome contract through the UpdateDON call on CapReg contract -// 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, - nodes deployment.Nodes, -) ([]mcms.Operation, error) { - // fetch DON ID for the chain - donID, err := DonIDForChain(capReg, ccipHome, chainSelector) - 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, - pluginConfig.PluginType, - pluginConfig, - [32]byte{}, - ) - if err != nil { - return nil, fmt.Errorf("pack set candidate call: %w", err) - } - - // set candidate call - updateDonTx, err := capReg.UpdateDON( - deployment.SimTransactOpts(), - donID, - nodes.PeerIDs(), - []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: CCIPCapabilityID, - Config: encodedSetCandidateCall, - }, - }, - false, - nodes.DefaultF(), - ) - if err != nil { - return nil, fmt.Errorf("update don w/ exec config: %w", err) - } - - return []mcms.Operation{{ - To: capReg.Address(), - Data: updateDonTx.Data(), - Value: big.NewInt(0), - }}, nil -} - -// 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 mcms.Operation{}, err - } - - 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) - - encodedPromotionCall, err := CCIPHomeABI.Pack( - "promoteCandidateAndRevokeActive", - donID, - pluginType, - allConfigs.CandidateConfig.ConfigDigest, - allConfigs.ActiveConfig.ConfigDigest, - ) - if err != nil { - return mcms.Operation{}, fmt.Errorf("pack promotion call: %w", err) - } - - updateDonTx, err := capReg.UpdateDON( - deployment.SimTransactOpts(), - donID, - nodes.PeerIDs(), - []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: CCIPCapabilityID, - Config: encodedPromotionCall, - }, - }, - false, - nodes.DefaultF(), - ) - if err != nil { - return mcms.Operation{}, fmt.Errorf("error creating updateDon op for donID(%d) and plugin type (%d): %w", donID, pluginType, err) - } - return mcms.Operation{ - To: capReg.Address(), - Data: updateDonTx.Data(), - Value: big.NewInt(0), - }, nil -} - -// 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, - nodes deployment.Nodes, -) ([]mcms.Operation, error) { - // fetch DON ID for the chain - donID, err := DonIDForChain(capReg, ccipHome, chainSelector) - if err != nil { - return nil, fmt.Errorf("fetch don id for chain: %w", err) - } - - var mcmsOps []mcms.Operation - updateCommitOp, err := PromoteCandidateOp(donID, uint8(cctypes.PluginTypeCCIPCommit), capReg, ccipHome, nodes) - if err != nil { - return nil, fmt.Errorf("promote candidate op: %w", err) - } - mcmsOps = append(mcmsOps, updateCommitOp) - - updateExecOp, err := PromoteCandidateOp(donID, uint8(cctypes.PluginTypeCCIPExec), capReg, ccipHome, nodes) - if err != nil { - return nil, fmt.Errorf("promote candidate op: %w", err) - } - mcmsOps = append(mcmsOps, updateExecOp) - - return mcmsOps, nil -} diff --git a/deployment/ccip/changeset/add_lane.go b/deployment/ccip/changeset/add_lane.go deleted file mode 100644 index 8af96277fc2..00000000000 --- a/deployment/ccip/changeset/add_lane.go +++ /dev/null @@ -1,138 +0,0 @@ -package ccipdeployment - -import ( - "encoding/hex" - "math/big" - - "github.com/ethereum/go-ethereum/common" - - "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ccipevm" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" - - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" -) - -type InitialPrices struct { - LinkPrice *big.Int // USD to the power of 18 (e18) per LINK - WethPrice *big.Int // USD to the power of 18 (e18) per WETH - GasPrice *big.Int // uint224 packed gas price in USD (112 for exec // 112 for da) -} - -var DefaultInitialPrices = InitialPrices{ - LinkPrice: deployment.E18Mult(20), - WethPrice: deployment.E18Mult(4000), - GasPrice: ToPackedFee(big.NewInt(8e14), big.NewInt(0)), -} - -func AddLaneWithDefaultPrices(e deployment.Environment, state CCIPOnChainState, from, to uint64) error { - return AddLane(e, state, from, to, DefaultInitialPrices) -} - -func AddLane(e deployment.Environment, state CCIPOnChainState, from, to uint64, initialPrices InitialPrices) error { - // TODO: Batch - tx, err := state.Chains[from].Router.ApplyRampUpdates(e.Chains[from].DeployerKey, []router.RouterOnRamp{ - { - DestChainSelector: to, - OnRamp: state.Chains[from].OnRamp.Address(), - }, - }, []router.RouterOffRamp{}, []router.RouterOffRamp{}) - if _, err := deployment.ConfirmIfNoError(e.Chains[from], tx, err); err != nil { - return err - } - tx, err = state.Chains[from].OnRamp.ApplyDestChainConfigUpdates(e.Chains[from].DeployerKey, - []onramp.OnRampDestChainConfigArgs{ - { - DestChainSelector: to, - Router: state.Chains[from].Router.Address(), - }, - }) - if _, err := deployment.ConfirmIfNoError(e.Chains[from], tx, err); err != nil { - return err - } - - _, err = state.Chains[from].FeeQuoter.UpdatePrices( - e.Chains[from].DeployerKey, fee_quoter.InternalPriceUpdates{ - TokenPriceUpdates: []fee_quoter.InternalTokenPriceUpdate{ - { - SourceToken: state.Chains[from].LinkToken.Address(), - UsdPerToken: initialPrices.LinkPrice, - }, - { - SourceToken: state.Chains[from].Weth9.Address(), - UsdPerToken: initialPrices.WethPrice, - }, - }, - GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{ - { - DestChainSelector: to, - UsdPerUnitGas: initialPrices.GasPrice, - }, - }}) - if _, err := deployment.ConfirmIfNoError(e.Chains[from], tx, err); err != nil { - return err - } - - // Enable dest in fee quoter - tx, err = state.Chains[from].FeeQuoter.ApplyDestChainConfigUpdates(e.Chains[from].DeployerKey, - []fee_quoter.FeeQuoterDestChainConfigArgs{ - { - DestChainSelector: to, - DestChainConfig: DefaultFeeQuoterDestChainConfig(), - }, - }) - if _, err := deployment.ConfirmIfNoError(e.Chains[from], tx, err); err != nil { - return err - } - - tx, err = state.Chains[to].OffRamp.ApplySourceChainConfigUpdates(e.Chains[to].DeployerKey, - []offramp.OffRampSourceChainConfigArgs{ - { - Router: state.Chains[to].Router.Address(), - SourceChainSelector: from, - IsEnabled: true, - OnRamp: common.LeftPadBytes(state.Chains[from].OnRamp.Address().Bytes(), 32), - }, - }) - if _, err := deployment.ConfirmIfNoError(e.Chains[to], tx, err); err != nil { - return err - } - tx, err = state.Chains[to].Router.ApplyRampUpdates(e.Chains[to].DeployerKey, []router.RouterOnRamp{}, []router.RouterOffRamp{}, []router.RouterOffRamp{ - { - SourceChainSelector: from, - OffRamp: state.Chains[to].OffRamp.Address(), - }, - }) - _, err = deployment.ConfirmIfNoError(e.Chains[to], tx, err) - return err -} - -func DefaultFeeQuoterDestChainConfig() fee_quoter.FeeQuoterDestChainConfig { - // https://github.com/smartcontractkit/ccip/blob/c4856b64bd766f1ddbaf5d13b42d3c4b12efde3a/contracts/src/v0.8/ccip/libraries/Internal.sol#L337-L337 - /* - ```Solidity - // bytes4(keccak256("CCIP ChainFamilySelector EVM")) - bytes4 public constant CHAIN_FAMILY_SELECTOR_EVM = 0x2812d52c; - ``` - */ - evmFamilySelector, _ := hex.DecodeString("2812d52c") - return fee_quoter.FeeQuoterDestChainConfig{ - IsEnabled: true, - MaxNumberOfTokensPerMsg: 10, - MaxDataBytes: 256, - MaxPerMsgGasLimit: 3_000_000, - DestGasOverhead: ccipevm.DestGasOverhead, - DefaultTokenFeeUSDCents: 1, - DestGasPerPayloadByte: ccipevm.CalldataGasPerByte, - DestDataAvailabilityOverheadGas: 100, - DestGasPerDataAvailabilityByte: 100, - DestDataAvailabilityMultiplierBps: 1, - DefaultTokenDestGasOverhead: 125_000, - DefaultTxGasLimit: 200_000, - GasMultiplierWeiPerEth: 11e17, // Gas multiplier in wei per eth is scaled by 1e18, so 11e17 is 1.1 = 110% - NetworkFeeUSDCents: 1, - ChainFamilySelector: [4]byte(evmFamilySelector), - } -} diff --git a/deployment/ccip/changeset/add_lane_test.go b/deployment/ccip/changeset/add_lane_test.go deleted file mode 100644 index 5edfdae1ab0..00000000000 --- a/deployment/ccip/changeset/add_lane_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package ccipdeployment - -import ( - "testing" - "time" - - "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" - "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" -) - -// TestAddLane covers the workflow of adding a lane between two chains and enabling it. -// It also covers the case where the onRamp is disabled on the OffRamp contract initially and then enabled. -func TestAddLane(t *testing.T) { - t.Parallel() - // We add more chains to the chainlink nodes than the number of chains where CCIP is deployed. - e := NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 4, 4) - // Here we have CR + nodes set up, but no CCIP contracts deployed. - state, err := LoadOnchainState(e.Env) - require.NoError(t, err) - - selectors := e.Env.AllChainSelectors() - // deploy CCIP contracts on two chains - chain1, chain2 := selectors[0], selectors[1] - - feeds := state.Chains[e.FeedChainSel].USDFeeds - tokenConfig := NewTestTokenConfig(feeds) - newAddresses := deployment.NewMemoryAddressBook() - err = DeployPrerequisiteChainContracts(e.Env, newAddresses, e.Env.AllChainSelectors()) - require.NoError(t, err) - require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) - - // Set up CCIP contracts and a DON per chain. - newAddresses = deployment.NewMemoryAddressBook() - err = DeployCCIPContracts(e.Env, newAddresses, DeployCCIPContractConfig{ - HomeChainSel: e.HomeChainSel, - FeedChainSel: e.FeedChainSel, - TokenConfig: tokenConfig, - MCMSConfig: NewTestMCMSConfig(t, e.Env), - ChainsToDeploy: []uint64{chain1, chain2}, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - }) - require.NoError(t, err) - require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) - - // We expect no lanes available on any chain. - state, err = LoadOnchainState(e.Env) - require.NoError(t, err) - for _, sel := range []uint64{chain1, chain2} { - chain := state.Chains[sel] - offRamps, err := chain.Router.GetOffRamps(nil) - require.NoError(t, err) - require.Len(t, offRamps, 0) - } - - replayBlocks, err := LatestBlocksByChain(testcontext.Get(t), e.Env.Chains) - require.NoError(t, err) - - // Add one lane from chain1 to chain 2 and send traffic. - require.NoError(t, AddLaneWithDefaultPrices(e.Env, state, chain1, chain2)) - - ReplayLogs(t, e.Env.Offchain, replayBlocks) - time.Sleep(30 * time.Second) - // disable the onRamp initially on OffRamp - disableRampTx, err := state.Chains[chain2].OffRamp.ApplySourceChainConfigUpdates(e.Env.Chains[chain2].DeployerKey, []offramp.OffRampSourceChainConfigArgs{ - { - Router: state.Chains[chain2].Router.Address(), - SourceChainSelector: chain1, - IsEnabled: false, - OnRamp: common.LeftPadBytes(state.Chains[chain1].OnRamp.Address().Bytes(), 32), - }, - }) - _, err = deployment.ConfirmIfNoError(e.Env.Chains[chain2], disableRampTx, err) - require.NoError(t, err) - - for _, sel := range []uint64{chain1, chain2} { - chain := state.Chains[sel] - offRamps, err := chain.Router.GetOffRamps(nil) - require.NoError(t, err) - if sel == chain2 { - require.Len(t, offRamps, 1) - srcCfg, err := chain.OffRamp.GetSourceChainConfig(nil, chain1) - require.NoError(t, err) - require.Equal(t, common.LeftPadBytes(state.Chains[chain1].OnRamp.Address().Bytes(), 32), srcCfg.OnRamp) - require.False(t, srcCfg.IsEnabled) - } else { - require.Len(t, offRamps, 0) - } - } - - latesthdr, err := e.Env.Chains[chain2].Client.HeaderByNumber(testcontext.Get(t), nil) - require.NoError(t, err) - 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 - msgSentEvent1 := 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), msgSentEvent1.SequenceNumber) - - // Add another lane - require.NoError(t, AddLaneWithDefaultPrices(e.Env, state, chain2, chain1)) - - // Send traffic on the second lane and it should succeed - latesthdr, err = e.Env.Chains[chain1].Client.HeaderByNumber(testcontext.Get(t), nil) - require.NoError(t, err) - startBlock2 := latesthdr.Number.Uint64() - msgSentEvent2 := 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), msgSentEvent2.SequenceNumber) - require.NoError(t, commonutils.JustError(ConfirmExecWithSeqNr(t, e.Env.Chains[chain2], e.Env.Chains[chain1], state.Chains[chain1].OffRamp, &startBlock2, msgSentEvent2.SequenceNumber))) - - // 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, msgSentEvent1.SequenceNumber, 30*time.Second) - - // enable the onRamp on OffRamp - enableRampTx, err := state.Chains[chain2].OffRamp.ApplySourceChainConfigUpdates(e.Env.Chains[chain2].DeployerKey, []offramp.OffRampSourceChainConfigArgs{ - { - Router: state.Chains[chain2].Router.Address(), - SourceChainSelector: chain1, - IsEnabled: true, - OnRamp: common.LeftPadBytes(state.Chains[chain1].OnRamp.Address().Bytes(), 32), - }, - }) - _, err = deployment.ConfirmIfNoError(e.Env.Chains[chain2], enableRampTx, err) - require.NoError(t, err) - - srcCfg, err := state.Chains[chain2].OffRamp.GetSourceChainConfig(nil, chain1) - require.NoError(t, err) - require.Equal(t, common.LeftPadBytes(state.Chains[chain1].OnRamp.Address().Bytes(), 32), srcCfg.OnRamp) - require.True(t, srcCfg.IsEnabled) - - // we need the replay here otherwise plugin is not able to locate the message - 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, commonutils.JustError(ConfirmExecWithSeqNr(t, e.Env.Chains[chain1], e.Env.Chains[chain2], state.Chains[chain2].OffRamp, &startBlock, msgSentEvent1.SequenceNumber))) -} diff --git a/deployment/ccip/changeset/consts.go b/deployment/ccip/changeset/consts.go deleted file mode 100644 index 48466bcef46..00000000000 --- a/deployment/ccip/changeset/consts.go +++ /dev/null @@ -1,11 +0,0 @@ -package ccipdeployment - -type TokenSymbol string - -const ( - LinkSymbol TokenSymbol = "LINK" - WethSymbol TokenSymbol = "WETH" - USDCSymbol TokenSymbol = "USDC" - LinkDecimals = 18 - WethDecimals = 18 -) diff --git a/deployment/ccip/changeset/deploy_home_chain.go b/deployment/ccip/changeset/deploy_home_chain.go deleted file mode 100644 index 9c7c65bc9dc..00000000000 --- a/deployment/ccip/changeset/deploy_home_chain.go +++ /dev/null @@ -1,1085 +0,0 @@ -package ccipdeployment - -import ( - "bytes" - "context" - "encoding/hex" - "encoding/json" - "fmt" - "math/big" - "os" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" - "golang.org/x/exp/maps" - - "github.com/smartcontractkit/chainlink-ccip/chainconfig" - "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" - "github.com/smartcontractkit/chainlink-ccip/pluginconfig" - - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/merklemulti" - - confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" - - "github.com/smartcontractkit/chainlink/deployment" - cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_home" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" - p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" -) - -const ( - NodeOperatorID = 1 - CapabilityLabelledName = "ccip" - CapabilityVersion = "v1.0.0" - - FirstBlockAge = 8 * time.Hour - RemoteGasPriceBatchWriteFrequency = 30 * time.Minute - TokenPriceBatchWriteFrequency = 30 * time.Minute - BatchGasLimit = 6_500_000 - RelativeBoostPerWaitHour = 1.5 - InflightCacheExpiry = 10 * time.Minute - RootSnoozeTime = 30 * time.Minute - BatchingStrategyID = 0 - DeltaProgress = 30 * time.Second - DeltaResend = 10 * time.Second - DeltaInitial = 20 * time.Second - DeltaRound = 2 * time.Second - DeltaGrace = 2 * time.Second - DeltaCertifiedCommitRequest = 10 * time.Second - DeltaStage = 10 * time.Second - Rmax = 3 - MaxDurationQuery = 500 * time.Millisecond - MaxDurationObservation = 5 * time.Second - MaxDurationShouldAcceptAttestedReport = 10 * time.Second - MaxDurationShouldTransmitAcceptedReport = 10 * time.Second -) - -var ( - CCIPCapabilityID = utils.Keccak256Fixed(MustABIEncode(`[{"type": "string"}, {"type": "string"}]`, CapabilityLabelledName, CapabilityVersion)) - CCIPHomeABI *abi.ABI -) - -func init() { - var err error - CCIPHomeABI, err = ccip_home.CCIPHomeMetaData.GetAbi() - if err != nil { - panic(err) - } -} - -func MustABIEncode(abiString string, args ...interface{}) []byte { - encoded, err := utils.ABIEncode(abiString, args...) - if err != nil { - panic(err) - } - return encoded -} - -// DeployCapReg deploys the CapabilitiesRegistry contract if it is not already deployed -// and returns a deployment.ContractDeploy struct with the address and contract instance. -func DeployCapReg( - lggr logger.Logger, - state CCIPOnChainState, - ab deployment.AddressBook, - chain deployment.Chain, -) (*deployment.ContractDeploy[*capabilities_registry.CapabilitiesRegistry], error) { - homeChainState, exists := state.Chains[chain.Selector] - if exists { - cr := homeChainState.CapabilityRegistry - if cr != nil { - lggr.Infow("Found CapabilitiesRegistry in chain state", "address", cr.Address().String()) - return &deployment.ContractDeploy[*capabilities_registry.CapabilitiesRegistry]{ - Address: cr.Address(), Contract: cr, Tv: deployment.NewTypeAndVersion(CapabilitiesRegistry, deployment.Version1_0_0), - }, nil - } - } - capReg, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*capabilities_registry.CapabilitiesRegistry] { - crAddr, tx, cr, err2 := capabilities_registry.DeployCapabilitiesRegistry( - chain.DeployerKey, - chain.Client, - ) - return deployment.ContractDeploy[*capabilities_registry.CapabilitiesRegistry]{ - Address: crAddr, Contract: cr, Tv: deployment.NewTypeAndVersion(CapabilitiesRegistry, deployment.Version1_0_0), Tx: tx, Err: err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy capreg", "err", err) - return nil, err - } - return capReg, nil -} - -func DeployHomeChain( - lggr logger.Logger, - e deployment.Environment, - ab deployment.AddressBook, - chain deployment.Chain, - rmnHomeStatic rmn_home.RMNHomeStaticConfig, - rmnHomeDynamic rmn_home.RMNHomeDynamicConfig, - nodeOps []capabilities_registry.CapabilitiesRegistryNodeOperator, - nodeP2PIDsPerNodeOpAdmin map[string][][32]byte, -) (*deployment.ContractDeploy[*capabilities_registry.CapabilitiesRegistry], error) { - // load existing state - state, err := LoadOnchainState(e) - if err != nil { - return nil, fmt.Errorf("failed to load onchain state: %w", err) - } - // Deploy CapabilitiesRegistry, CCIPHome, RMNHome - capReg, err := DeployCapReg(lggr, state, ab, chain) - if err != nil { - return nil, err - } - - lggr.Infow("deployed/connected to capreg", "addr", capReg.Address) - ccipHome, err := deployment.DeployContract( - lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*ccip_home.CCIPHome] { - ccAddr, tx, cc, err2 := ccip_home.DeployCCIPHome( - chain.DeployerKey, - chain.Client, - capReg.Address, - ) - return deployment.ContractDeploy[*ccip_home.CCIPHome]{ - Address: ccAddr, Tv: deployment.NewTypeAndVersion(CCIPHome, deployment.Version1_6_0_dev), Tx: tx, Err: err2, Contract: cc, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy CCIPHome", "err", err) - return nil, err - } - lggr.Infow("deployed CCIPHome", "addr", ccipHome.Address) - - rmnHome, err := deployment.DeployContract( - lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*rmn_home.RMNHome] { - rmnAddr, tx, rmn, err2 := rmn_home.DeployRMNHome( - chain.DeployerKey, - chain.Client, - ) - return deployment.ContractDeploy[*rmn_home.RMNHome]{ - Address: rmnAddr, Tv: deployment.NewTypeAndVersion(RMNHome, deployment.Version1_6_0_dev), Tx: tx, Err: err2, Contract: rmn, - } - }, - ) - if err != nil { - lggr.Errorw("Failed to deploy RMNHome", "err", err) - return nil, err - } - lggr.Infow("deployed RMNHome", "addr", rmnHome.Address) - - // considering the RMNHome is recently deployed, there is no digest to overwrite - tx, err := rmnHome.Contract.SetCandidate(chain.DeployerKey, rmnHomeStatic, rmnHomeDynamic, [32]byte{}) - if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - lggr.Errorw("Failed to set candidate on RMNHome", "err", err) - return nil, err - } - - rmnCandidateDigest, err := rmnHome.Contract.GetCandidateDigest(nil) - if err != nil { - lggr.Errorw("Failed to get RMNHome candidate digest", "err", err) - return nil, err - } - - tx, err = rmnHome.Contract.PromoteCandidateAndRevokeActive(chain.DeployerKey, rmnCandidateDigest, [32]byte{}) - if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - lggr.Errorw("Failed to promote candidate and revoke active on RMNHome", "err", err) - return nil, err - } - - rmnActiveDigest, err := rmnHome.Contract.GetActiveDigest(nil) - if err != nil { - lggr.Errorw("Failed to get RMNHome active digest", "err", err) - return nil, err - } - lggr.Infow("Got rmn home active digest", "digest", rmnActiveDigest) - - if rmnActiveDigest != rmnCandidateDigest { - lggr.Errorw("RMNHome active digest does not match previously candidate digest", - "active", rmnActiveDigest, "candidate", rmnCandidateDigest) - return nil, errors.New("RMNHome active digest does not match candidate digest") - } - - tx, err = capReg.Contract.AddCapabilities(chain.DeployerKey, []capabilities_registry.CapabilitiesRegistryCapability{ - { - LabelledName: CapabilityLabelledName, - Version: CapabilityVersion, - CapabilityType: 2, // consensus. not used (?) - ResponseType: 0, // report. not used (?) - ConfigurationContract: ccipHome.Address, - }, - }) - if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - lggr.Errorw("Failed to add capabilities", "err", err) - return nil, err - } - - tx, err = capReg.Contract.AddNodeOperators(chain.DeployerKey, nodeOps) - txBlockNum, err := deployment.ConfirmIfNoError(chain, tx, err) - if err != nil { - lggr.Errorw("Failed to add node operators", "err", err) - return nil, err - } - addedEvent, err := capReg.Contract.FilterNodeOperatorAdded(&bind.FilterOpts{ - Start: txBlockNum, - Context: context.Background(), - }, nil, nil) - if err != nil { - lggr.Errorw("Failed to filter NodeOperatorAdded event", "err", err) - return capReg, err - } - // Need to fetch nodeoperators ids to be able to add nodes for corresponding node operators - p2pIDsByNodeOpId := make(map[uint32][][32]byte) - for addedEvent.Next() { - for nopName, p2pId := range nodeP2PIDsPerNodeOpAdmin { - if addedEvent.Event.Name == nopName { - lggr.Infow("Added node operator", "admin", addedEvent.Event.Admin, "name", addedEvent.Event.Name) - p2pIDsByNodeOpId[addedEvent.Event.NodeOperatorId] = p2pId - } - } - } - if len(p2pIDsByNodeOpId) != len(nodeP2PIDsPerNodeOpAdmin) { - lggr.Errorw("Failed to add all node operators", "added", maps.Keys(p2pIDsByNodeOpId), "expected", maps.Keys(nodeP2PIDsPerNodeOpAdmin)) - return capReg, errors.New("failed to add all node operators") - } - // Adds initial set of nodes to CR, who all have the CCIP capability - if err := AddNodes(lggr, capReg.Contract, chain, p2pIDsByNodeOpId); err != nil { - return capReg, err - } - return capReg, nil -} - -// getNodeOperatorIDMap returns a map of node operator names to their IDs -// If maxNops is greater than the number of node operators, it will return all node operators -func getNodeOperatorIDMap(capReg *capabilities_registry.CapabilitiesRegistry, maxNops uint32) (map[string]uint32, error) { - nopIdByName := make(map[string]uint32) - operators, err := capReg.GetNodeOperators(nil) - if err != nil { - return nil, err - } - if len(operators) < int(maxNops) { - maxNops = uint32(len(operators)) - } - for i := uint32(1); i <= maxNops; i++ { - operator, err := capReg.GetNodeOperator(nil, i) - if err != nil { - return nil, err - } - nopIdByName[operator.Name] = i - } - return nopIdByName, nil -} - -func isEqualCapabilitiesRegistryNodeParams(a, b capabilities_registry.CapabilitiesRegistryNodeParams) (bool, error) { - aBytes, err := json.Marshal(a) - if err != nil { - return false, err - } - bBytes, err := json.Marshal(b) - if err != nil { - return false, err - } - return bytes.Equal(aBytes, bBytes), nil -} - -func AddNodes( - lggr logger.Logger, - capReg *capabilities_registry.CapabilitiesRegistry, - chain deployment.Chain, - p2pIDsByNodeOpId map[uint32][][32]byte, -) error { - var nodeParams []capabilities_registry.CapabilitiesRegistryNodeParams - nodes, err := capReg.GetNodes(nil) - if err != nil { - return err - } - existingNodeParams := make(map[p2ptypes.PeerID]capabilities_registry.CapabilitiesRegistryNodeParams) - for _, node := range nodes { - existingNodeParams[node.P2pId] = capabilities_registry.CapabilitiesRegistryNodeParams{ - NodeOperatorId: node.NodeOperatorId, - Signer: node.Signer, - P2pId: node.P2pId, - HashedCapabilityIds: node.HashedCapabilityIds, - } - } - for nopID, p2pIDs := range p2pIDsByNodeOpId { - for _, p2pID := range p2pIDs { - // if any p2pIDs are empty throw error - if bytes.Equal(p2pID[:], make([]byte, 32)) { - return errors.Wrapf(errors.New("empty p2pID"), "p2pID: %x selector: %d", p2pID, chain.Selector) - } - nodeParam := capabilities_registry.CapabilitiesRegistryNodeParams{ - NodeOperatorId: nopID, - Signer: p2pID, // Not used in tests - P2pId: p2pID, - EncryptionPublicKey: p2pID, // Not used in tests - HashedCapabilityIds: [][32]byte{CCIPCapabilityID}, - } - if existing, ok := existingNodeParams[p2pID]; ok { - if isEqual, err := isEqualCapabilitiesRegistryNodeParams(existing, nodeParam); err != nil && isEqual { - lggr.Infow("Node already exists", "p2pID", p2pID) - continue - } - } - - nodeParams = append(nodeParams, nodeParam) - } - } - if len(nodeParams) == 0 { - lggr.Infow("No new nodes to add") - return nil - } - tx, err := capReg.AddNodes(chain.DeployerKey, nodeParams) - if err != nil { - lggr.Errorw("Failed to add nodes", "err", deployment.MaybeDataErr(err)) - return err - } - _, err = chain.Confirm(tx) - return err -} - -func SetupConfigInfo(chainSelector uint64, readers [][32]byte, fChain uint8, cfg []byte) ccip_home.CCIPHomeChainConfigArgs { - return ccip_home.CCIPHomeChainConfigArgs{ - ChainSelector: chainSelector, - ChainConfig: ccip_home.CCIPHomeChainConfig{ - Readers: readers, - FChain: fChain, - Config: cfg, - }, - } -} - -func AddChainConfig( - lggr logger.Logger, - h deployment.Chain, - ccipConfig *ccip_home.CCIPHome, - chainSelector uint64, - p2pIDs [][32]byte, -) (ccip_home.CCIPHomeChainConfigArgs, error) { - // First Add ChainConfig that includes all p2pIDs as readers - encodedExtraChainConfig, err := chainconfig.EncodeChainConfig(chainconfig.ChainConfig{ - GasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(1000), - DAGasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(0), - OptimisticConfirmations: 1, - }) - if err != nil { - return ccip_home.CCIPHomeChainConfigArgs{}, err - } - chainConfig := SetupConfigInfo(chainSelector, p2pIDs, uint8(len(p2pIDs)/3), encodedExtraChainConfig) - tx, err := ccipConfig.ApplyChainConfigUpdates(h.DeployerKey, nil, []ccip_home.CCIPHomeChainConfigArgs{ - chainConfig, - }) - if _, err := deployment.ConfirmIfNoError(h, tx, err); err != nil { - return ccip_home.CCIPHomeChainConfigArgs{}, err - } - lggr.Infow("Applied chain config updates", "chainConfig", chainConfig) - return chainConfig, nil -} - -func BuildOCR3ConfigForCCIPHome( - ocrSecrets deployment.OCRSecrets, - offRamp *offramp.OffRamp, - dest deployment.Chain, - feedChainSel uint64, - tokenInfo map[ccipocr3.UnknownEncodedAddress]pluginconfig.TokenInfo, - nodes deployment.Nodes, - rmnHomeAddress common.Address, - configs []pluginconfig.TokenDataObserverConfig, -) (map[cctypes.PluginType]ccip_home.CCIPHomeOCR3Config, error) { - p2pIDs := nodes.PeerIDs() - // Get OCR3 Config from helper - var schedule []int - var oracles []confighelper2.OracleIdentityExtra - for _, node := range nodes { - schedule = append(schedule, 1) - cfg := node.SelToOCRConfig[dest.Selector] - oracles = append(oracles, confighelper2.OracleIdentityExtra{ - OracleIdentity: confighelper2.OracleIdentity{ - OnchainPublicKey: cfg.OnchainPublicKey, - TransmitAccount: cfg.TransmitAccount, - OffchainPublicKey: cfg.OffchainPublicKey, - PeerID: cfg.PeerID.String()[4:], - }, ConfigEncryptionPublicKey: cfg.ConfigEncryptionPublicKey, - }) - } - - // Add DON on capability registry contract - ocr3Configs := make(map[cctypes.PluginType]ccip_home.CCIPHomeOCR3Config) - for _, pluginType := range []cctypes.PluginType{cctypes.PluginTypeCCIPCommit, cctypes.PluginTypeCCIPExec} { - var encodedOffchainConfig []byte - var err2 error - if pluginType == cctypes.PluginTypeCCIPCommit { - encodedOffchainConfig, err2 = pluginconfig.EncodeCommitOffchainConfig(pluginconfig.CommitOffchainConfig{ - RemoteGasPriceBatchWriteFrequency: *commonconfig.MustNewDuration(RemoteGasPriceBatchWriteFrequency), - TokenPriceBatchWriteFrequency: *commonconfig.MustNewDuration(TokenPriceBatchWriteFrequency), - PriceFeedChainSelector: ccipocr3.ChainSelector(feedChainSel), - TokenInfo: tokenInfo, - NewMsgScanBatchSize: merklemulti.MaxNumberTreeLeaves, - MaxReportTransmissionCheckAttempts: 5, - MaxMerkleTreeSize: merklemulti.MaxNumberTreeLeaves, - SignObservationPrefix: "chainlink ccip 1.6 rmn observation", - RMNEnabled: os.Getenv("ENABLE_RMN") == "true", // only enabled in manual test - }) - } else { - encodedOffchainConfig, err2 = pluginconfig.EncodeExecuteOffchainConfig(pluginconfig.ExecuteOffchainConfig{ - BatchGasLimit: BatchGasLimit, - RelativeBoostPerWaitHour: RelativeBoostPerWaitHour, - MessageVisibilityInterval: *commonconfig.MustNewDuration(FirstBlockAge), - InflightCacheExpiry: *commonconfig.MustNewDuration(InflightCacheExpiry), - RootSnoozeTime: *commonconfig.MustNewDuration(RootSnoozeTime), - BatchingStrategyID: BatchingStrategyID, - TokenDataObservers: configs, - }) - } - if err2 != nil { - return nil, err2 - } - signers, transmitters, configF, _, offchainConfigVersion, offchainConfig, err2 := ocr3confighelper.ContractSetConfigArgsDeterministic( - ocrSecrets.EphemeralSk, - ocrSecrets.SharedSecret, - DeltaProgress, - DeltaResend, - DeltaInitial, - DeltaRound, - DeltaGrace, - DeltaCertifiedCommitRequest, - DeltaStage, - Rmax, - schedule, - oracles, - encodedOffchainConfig, - nil, // maxDurationInitialization - MaxDurationQuery, - MaxDurationObservation, - MaxDurationShouldAcceptAttestedReport, - MaxDurationShouldTransmitAcceptedReport, - int(nodes.DefaultF()), - []byte{}, // empty OnChainConfig - ) - if err2 != nil { - return nil, err2 - } - - signersBytes := make([][]byte, len(signers)) - for i, signer := range signers { - signersBytes[i] = signer - } - - transmittersBytes := make([][]byte, len(transmitters)) - for i, transmitter := range transmitters { - parsed, err2 := common.ParseHexOrString(string(transmitter)) - if err2 != nil { - return nil, err2 - } - transmittersBytes[i] = parsed - } - - var ocrNodes []ccip_home.CCIPHomeOCR3Node - for i := range nodes { - ocrNodes = append(ocrNodes, ccip_home.CCIPHomeOCR3Node{ - P2pId: p2pIDs[i], - SignerKey: signersBytes[i], - TransmitterKey: transmittersBytes[i], - }) - } - - _, ok := ocr3Configs[pluginType] - if ok { - return nil, fmt.Errorf("pluginType %s already exists in ocr3Configs", pluginType.String()) - } - - ocr3Configs[pluginType] = ccip_home.CCIPHomeOCR3Config{ - PluginType: uint8(pluginType), - ChainSelector: dest.Selector, - FRoleDON: configF, - OffchainConfigVersion: offchainConfigVersion, - OfframpAddress: offRamp.Address().Bytes(), - Nodes: ocrNodes, - OffchainConfig: offchainConfig, - RmnHomeAddress: rmnHomeAddress.Bytes(), - } - } - - return ocr3Configs, nil -} - -func LatestCCIPDON(registry *capabilities_registry.CapabilitiesRegistry) (*capabilities_registry.CapabilitiesRegistryDONInfo, error) { - dons, err := registry.GetDONs(nil) - if err != nil { - return nil, err - } - var ccipDON capabilities_registry.CapabilitiesRegistryDONInfo - for _, don := range dons { - if len(don.CapabilityConfigurations) == 1 && - don.CapabilityConfigurations[0].CapabilityId == CCIPCapabilityID && - don.Id > ccipDON.Id { - ccipDON = don - } - } - return &ccipDON, nil -} - -// DonIDForChain returns the DON ID for the chain with the given selector -// It looks up with the CCIPHome contract to find the OCR3 configs for the DONs, and returns the DON ID for the chain matching with the given selector from the OCR3 configs -func DonIDForChain(registry *capabilities_registry.CapabilitiesRegistry, ccipHome *ccip_home.CCIPHome, chainSelector uint64) (uint32, error) { - dons, err := registry.GetDONs(nil) - 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 { - configs, err := ccipHome.GetAllConfigs(nil, don.Id, uint8(cctypes.PluginTypeCCIPCommit)) - if err != nil { - return 0, err - } - if configs.ActiveConfig.Config.ChainSelector == chainSelector || configs.CandidateConfig.Config.ChainSelector == chainSelector { - return don.Id, nil - } - } - } - return 0, fmt.Errorf("no DON found for chain %d", chainSelector) -} - -func BuildSetOCR3ConfigArgs( - donID uint32, - ccipHome *ccip_home.CCIPHome, - destSelector uint64, -) ([]offramp.MultiOCR3BaseOCRConfigArgs, error) { - var offrampOCR3Configs []offramp.MultiOCR3BaseOCRConfigArgs - for _, pluginType := range []cctypes.PluginType{cctypes.PluginTypeCCIPCommit, cctypes.PluginTypeCCIPExec} { - ocrConfig, err2 := ccipHome.GetAllConfigs(&bind.CallOpts{ - Context: context.Background(), - }, donID, uint8(pluginType)) - if err2 != nil { - return nil, err2 - } - - fmt.Printf("pluginType: %s, destSelector: %d, donID: %d, activeConfig digest: %x, candidateConfig digest: %x\n", - pluginType.String(), destSelector, donID, ocrConfig.ActiveConfig.ConfigDigest, ocrConfig.CandidateConfig.ConfigDigest) - - // we expect only an active config and no candidate config. - if ocrConfig.ActiveConfig.ConfigDigest == [32]byte{} || ocrConfig.CandidateConfig.ConfigDigest != [32]byte{} { - return nil, fmt.Errorf("invalid OCR3 config state, expected active config and no candidate config, donID: %d", donID) - } - - activeConfig := ocrConfig.ActiveConfig - var signerAddresses []common.Address - var transmitterAddresses []common.Address - for _, node := range activeConfig.Config.Nodes { - signerAddresses = append(signerAddresses, common.BytesToAddress(node.SignerKey)) - transmitterAddresses = append(transmitterAddresses, common.BytesToAddress(node.TransmitterKey)) - } - - offrampOCR3Configs = append(offrampOCR3Configs, offramp.MultiOCR3BaseOCRConfigArgs{ - ConfigDigest: activeConfig.ConfigDigest, - OcrPluginType: uint8(pluginType), - F: activeConfig.Config.FRoleDON, - IsSignatureVerificationEnabled: pluginType == cctypes.PluginTypeCCIPCommit, - Signers: signerAddresses, - Transmitters: transmitterAddresses, - }) - } - return offrampOCR3Configs, nil -} - -// CreateDON creates one DON with 2 plugins (commit and exec) -// It first set a new candidate for the DON with the first plugin type and AddDON on capReg -// Then for subsequent operations it uses UpdateDON to promote the first plugin to the active deployment -// and to set candidate and promote it for the second plugin -func CreateDON( - lggr logger.Logger, - capReg *capabilities_registry.CapabilitiesRegistry, - ccipHome *ccip_home.CCIPHome, - ocr3Configs map[cctypes.PluginType]ccip_home.CCIPHomeOCR3Config, - home deployment.Chain, - newChainSel uint64, - nodes deployment.Nodes, -) error { - commitConfig, ok := ocr3Configs[cctypes.PluginTypeCCIPCommit] - if !ok { - return fmt.Errorf("missing commit plugin in ocr3Configs") - } - - execConfig, ok := ocr3Configs[cctypes.PluginTypeCCIPExec] - if !ok { - return fmt.Errorf("missing exec plugin in ocr3Configs") - } - - latestDon, err := LatestCCIPDON(capReg) - if err != nil { - return err - } - - donID := latestDon.Id + 1 - - err = setupCommitDON(donID, commitConfig, capReg, home, nodes, ccipHome) - if err != nil { - return fmt.Errorf("setup commit don: %w", err) - } - - // TODO: bug in contract causing this to not work as expected. - err = setupExecDON(donID, execConfig, capReg, home, nodes, ccipHome) - if err != nil { - return fmt.Errorf("setup exec don: %w", err) - } - return ValidateCCIPHomeConfigSetUp(capReg, ccipHome, newChainSel) -} - -func setupExecDON( - donID uint32, - execConfig ccip_home.CCIPHomeOCR3Config, - capReg *capabilities_registry.CapabilitiesRegistry, - home deployment.Chain, - nodes deployment.Nodes, - ccipHome *ccip_home.CCIPHome, -) error { - encodedSetCandidateCall, err := CCIPHomeABI.Pack( - "setCandidate", - donID, - execConfig.PluginType, - execConfig, - [32]byte{}, - ) - if err != nil { - return fmt.Errorf("pack set candidate call: %w", err) - } - - // set candidate call - tx, err := capReg.UpdateDON( - home.DeployerKey, - donID, - nodes.PeerIDs(), - []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: CCIPCapabilityID, - Config: encodedSetCandidateCall, - }, - }, - false, - nodes.DefaultF(), - ) - if err != nil { - return fmt.Errorf("update don w/ exec config: %w", err) - } - - if _, err := deployment.ConfirmIfNoError(home, tx, err); err != nil { - return fmt.Errorf("confirm update don w/ exec config: %w", err) - } - - execCandidateDigest, err := ccipHome.GetCandidateDigest(nil, donID, execConfig.PluginType) - if err != nil { - return fmt.Errorf("get exec candidate digest 1st time: %w", err) - } - - if execCandidateDigest == [32]byte{} { - return fmt.Errorf("candidate digest is empty, expected nonempty") - } - - // promote candidate call - encodedPromotionCall, err := CCIPHomeABI.Pack( - "promoteCandidateAndRevokeActive", - donID, - execConfig.PluginType, - execCandidateDigest, - [32]byte{}, - ) - if err != nil { - return fmt.Errorf("pack promotion call: %w", err) - } - - tx, err = capReg.UpdateDON( - home.DeployerKey, - donID, - nodes.PeerIDs(), - []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: CCIPCapabilityID, - Config: encodedPromotionCall, - }, - }, - false, - nodes.DefaultF(), - ) - if err != nil { - return fmt.Errorf("update don w/ exec config: %w", err) - } - bn, err := deployment.ConfirmIfNoError(home, tx, err) - if err != nil { - return fmt.Errorf("confirm update don w/ exec config: %w", err) - } - if bn == 0 { - return fmt.Errorf("UpdateDON tx not confirmed") - } - // check if candidate digest is promoted - pEvent, err := ccipHome.FilterConfigPromoted(&bind.FilterOpts{ - Context: context.Background(), - Start: bn, - }, [][32]byte{execCandidateDigest}) - if err != nil { - return fmt.Errorf("filter exec config promoted: %w", err) - } - if !pEvent.Next() { - return fmt.Errorf("exec config not promoted") - } - // check that candidate digest is empty. - execCandidateDigest, err = ccipHome.GetCandidateDigest(nil, donID, execConfig.PluginType) - if err != nil { - return fmt.Errorf("get exec candidate digest 2nd time: %w", err) - } - - if execCandidateDigest != [32]byte{} { - return fmt.Errorf("candidate digest is nonempty after promotion, expected empty") - } - - // check that active digest is non-empty. - execActiveDigest, err := ccipHome.GetActiveDigest(nil, donID, uint8(cctypes.PluginTypeCCIPExec)) - if err != nil { - return fmt.Errorf("get active exec digest: %w", err) - } - - if execActiveDigest == [32]byte{} { - return fmt.Errorf("active exec digest is empty, expected nonempty") - } - - execConfigs, err := ccipHome.GetAllConfigs(nil, donID, uint8(cctypes.PluginTypeCCIPExec)) - if err != nil { - return fmt.Errorf("get all exec configs 2nd time: %w", err) - } - - // print the above info - fmt.Printf("completed exec DON creation and promotion: donID: %d execCandidateDigest: %x, execActiveDigest: %x, execCandidateDigestFromGetAllConfigs: %x, execActiveDigestFromGetAllConfigs: %x\n", - donID, execCandidateDigest, execActiveDigest, execConfigs.CandidateConfig.ConfigDigest, execConfigs.ActiveConfig.ConfigDigest) - - return nil -} - -// 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 NewDonWithCandidateOp( - donID uint32, - pluginConfig ccip_home.CCIPHomeOCR3Config, - capReg *capabilities_registry.CapabilitiesRegistry, - nodes deployment.Nodes, -) (mcms.Operation, error) { - encodedSetCandidateCall, err := CCIPHomeABI.Pack( - "setCandidate", - donID, - pluginConfig.PluginType, - pluginConfig, - [32]byte{}, - ) - if err != nil { - return mcms.Operation{}, fmt.Errorf("pack set candidate call: %w", err) - } - addDonTx, err := capReg.AddDON(deployment.SimTransactOpts(), nodes.PeerIDs(), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: CCIPCapabilityID, - Config: encodedSetCandidateCall, - }, - }, false, false, nodes.DefaultF()) - if err != nil { - return mcms.Operation{}, fmt.Errorf("could not generate add don tx w/ commit config: %w", err) - } - return mcms.Operation{ - To: capReg.Address(), - Data: addDonTx.Data(), - Value: big.NewInt(0), - }, nil -} - -// ValidateCCIPHomeConfigSetUp checks that the commit and exec active and candidate configs are set up correctly -func ValidateCCIPHomeConfigSetUp( - capReg *capabilities_registry.CapabilitiesRegistry, - ccipHome *ccip_home.CCIPHome, - chainSel uint64, -) error { - // fetch DONID - donID, err := DonIDForChain(capReg, ccipHome, chainSel) - if err != nil { - return fmt.Errorf("fetch don id for chain: %w", err) - } - // final sanity checks on configs. - commitConfigs, err := ccipHome.GetAllConfigs(&bind.CallOpts{ - //Pending: true, - }, donID, uint8(cctypes.PluginTypeCCIPCommit)) - if err != nil { - return fmt.Errorf("get all commit configs: %w", err) - } - commitActiveDigest, err := ccipHome.GetActiveDigest(nil, donID, uint8(cctypes.PluginTypeCCIPCommit)) - if err != nil { - return fmt.Errorf("get active commit digest: %w", err) - } - commitCandidateDigest, err := ccipHome.GetCandidateDigest(nil, donID, uint8(cctypes.PluginTypeCCIPCommit)) - if err != nil { - return fmt.Errorf("get commit candidate digest: %w", err) - } - if commitConfigs.ActiveConfig.ConfigDigest == [32]byte{} { - return fmt.Errorf( - "active config digest is empty for commit, expected nonempty, donID: %d, cfg: %+v, config digest from GetActiveDigest call: %x, config digest from GetCandidateDigest call: %x", - donID, commitConfigs.ActiveConfig, commitActiveDigest, commitCandidateDigest) - } - if commitConfigs.CandidateConfig.ConfigDigest != [32]byte{} { - return fmt.Errorf( - "candidate config digest is nonempty for commit, expected empty, donID: %d, cfg: %+v, config digest from GetCandidateDigest call: %x, config digest from GetActiveDigest call: %x", - donID, commitConfigs.CandidateConfig, commitCandidateDigest, commitActiveDigest) - } - - execConfigs, err := ccipHome.GetAllConfigs(nil, donID, uint8(cctypes.PluginTypeCCIPExec)) - if err != nil { - return fmt.Errorf("get all exec configs: %w", err) - } - if execConfigs.ActiveConfig.ConfigDigest == [32]byte{} { - return fmt.Errorf("active config digest is empty for exec, expected nonempty, cfg: %v", execConfigs.ActiveConfig) - } - if execConfigs.CandidateConfig.ConfigDigest != [32]byte{} { - return fmt.Errorf("candidate config digest is nonempty for exec, expected empty, cfg: %v", execConfigs.CandidateConfig) - } - return nil -} - -func setupCommitDON( - donID uint32, - commitConfig ccip_home.CCIPHomeOCR3Config, - capReg *capabilities_registry.CapabilitiesRegistry, - home deployment.Chain, - nodes deployment.Nodes, - ccipHome *ccip_home.CCIPHome, -) error { - encodedSetCandidateCall, err := CCIPHomeABI.Pack( - "setCandidate", - donID, - commitConfig.PluginType, - commitConfig, - [32]byte{}, - ) - if err != nil { - return fmt.Errorf("pack set candidate call: %w", err) - } - tx, err := capReg.AddDON(home.DeployerKey, nodes.PeerIDs(), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: CCIPCapabilityID, - Config: encodedSetCandidateCall, - }, - }, false, false, nodes.DefaultF()) - if err != nil { - return fmt.Errorf("add don w/ commit config: %w", err) - } - - if _, err := deployment.ConfirmIfNoError(home, tx, err); err != nil { - return fmt.Errorf("confirm add don w/ commit config: %w", err) - } - - commitCandidateDigest, err := ccipHome.GetCandidateDigest(nil, donID, commitConfig.PluginType) - if err != nil { - return fmt.Errorf("get commit candidate digest: %w", err) - } - - if commitCandidateDigest == [32]byte{} { - return fmt.Errorf("candidate digest is empty, expected nonempty") - } - fmt.Printf("commit candidate digest after setCandidate: %x\n", commitCandidateDigest) - - encodedPromotionCall, err := CCIPHomeABI.Pack( - "promoteCandidateAndRevokeActive", - donID, - commitConfig.PluginType, - commitCandidateDigest, - [32]byte{}, - ) - if err != nil { - return fmt.Errorf("pack promotion call: %w", err) - } - - tx, err = capReg.UpdateDON( - home.DeployerKey, - donID, - nodes.PeerIDs(), - []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: CCIPCapabilityID, - Config: encodedPromotionCall, - }, - }, - false, - nodes.DefaultF(), - ) - if err != nil { - return fmt.Errorf("update don w/ commit config: %w", err) - } - - if _, err := deployment.ConfirmIfNoError(home, tx, err); err != nil { - return fmt.Errorf("confirm update don w/ commit config: %w", err) - } - - // check that candidate digest is empty. - commitCandidateDigest, err = ccipHome.GetCandidateDigest(nil, donID, commitConfig.PluginType) - if err != nil { - return fmt.Errorf("get commit candidate digest 2nd time: %w", err) - } - - if commitCandidateDigest != [32]byte{} { - return fmt.Errorf("candidate digest is nonempty after promotion, expected empty") - } - - // check that active digest is non-empty. - commitActiveDigest, err := ccipHome.GetActiveDigest(nil, donID, uint8(cctypes.PluginTypeCCIPCommit)) - if err != nil { - return fmt.Errorf("get active commit digest: %w", err) - } - - if commitActiveDigest == [32]byte{} { - return fmt.Errorf("active commit digest is empty, expected nonempty") - } - - commitConfigs, err := ccipHome.GetAllConfigs(nil, donID, uint8(cctypes.PluginTypeCCIPCommit)) - if err != nil { - return fmt.Errorf("get all commit configs 2nd time: %w", err) - } - - // print the above information - fmt.Printf("completed commit DON creation and promotion: donID: %d, commitCandidateDigest: %x, commitActiveDigest: %x, commitCandidateDigestFromGetAllConfigs: %x, commitActiveDigestFromGetAllConfigs: %x\n", - donID, commitCandidateDigest, commitActiveDigest, commitConfigs.CandidateConfig.ConfigDigest, commitConfigs.ActiveConfig.ConfigDigest) - - return nil -} - -func AddDON( - lggr logger.Logger, - ocrSecrets deployment.OCRSecrets, - capReg *capabilities_registry.CapabilitiesRegistry, - ccipHome *ccip_home.CCIPHome, - rmnHomeAddress common.Address, - offRamp *offramp.OffRamp, - feedChainSel uint64, - // Token address on Dest chain to aggregate address on feed chain - tokenInfo map[ccipocr3.UnknownEncodedAddress]pluginconfig.TokenInfo, - dest deployment.Chain, - home deployment.Chain, - nodes deployment.Nodes, - tokenConfigs []pluginconfig.TokenDataObserverConfig, -) error { - ocrConfigs, err := BuildOCR3ConfigForCCIPHome( - ocrSecrets, offRamp, dest, feedChainSel, tokenInfo, nodes, rmnHomeAddress, tokenConfigs) - if err != nil { - return err - } - err = CreateDON(lggr, capReg, ccipHome, ocrConfigs, home, dest.Selector, nodes) - if err != nil { - return err - } - don, err := LatestCCIPDON(capReg) - if err != nil { - return err - } - lggr.Infow("Added DON", "donID", don.Id) - - offrampOCR3Configs, err := BuildSetOCR3ConfigArgs(don.Id, ccipHome, dest.Selector) - if err != nil { - return err - } - lggr.Infow("Setting OCR3 Configs", - "offrampOCR3Configs", offrampOCR3Configs, - "configDigestCommit", hex.EncodeToString(offrampOCR3Configs[cctypes.PluginTypeCCIPCommit].ConfigDigest[:]), - "configDigestExec", hex.EncodeToString(offrampOCR3Configs[cctypes.PluginTypeCCIPExec].ConfigDigest[:]), - "chainSelector", dest.Selector, - ) - - tx, err := offRamp.SetOCR3Configs(dest.DeployerKey, offrampOCR3Configs) - if _, err := deployment.ConfirmIfNoError(dest, tx, err); err != nil { - return err - } - - mapOfframpOCR3Configs := make(map[cctypes.PluginType]offramp.MultiOCR3BaseOCRConfigArgs) - for _, config := range offrampOCR3Configs { - mapOfframpOCR3Configs[cctypes.PluginType(config.OcrPluginType)] = config - } - - for _, pluginType := range []cctypes.PluginType{cctypes.PluginTypeCCIPCommit, cctypes.PluginTypeCCIPExec} { - ocrConfig, err := offRamp.LatestConfigDetails(&bind.CallOpts{ - Context: context.Background(), - }, uint8(pluginType)) - if err != nil { - return err - } - // TODO: assertions to be done as part of full state - // resprentation validation CCIP-3047 - if mapOfframpOCR3Configs[pluginType].ConfigDigest != ocrConfig.ConfigInfo.ConfigDigest { - return fmt.Errorf("%s OCR3 config digest mismatch", pluginType.String()) - } - if mapOfframpOCR3Configs[pluginType].F != ocrConfig.ConfigInfo.F { - return fmt.Errorf("%s OCR3 config F mismatch", pluginType.String()) - } - if mapOfframpOCR3Configs[pluginType].IsSignatureVerificationEnabled != ocrConfig.ConfigInfo.IsSignatureVerificationEnabled { - return fmt.Errorf("%s OCR3 config signature verification mismatch", pluginType.String()) - } - if pluginType == cctypes.PluginTypeCCIPCommit { - // only commit will set signers, exec doesn't need them. - for i, signer := range mapOfframpOCR3Configs[pluginType].Signers { - if !bytes.Equal(signer.Bytes(), ocrConfig.Signers[i].Bytes()) { - return fmt.Errorf("%s OCR3 config signer mismatch", pluginType.String()) - } - } - } - for i, transmitter := range mapOfframpOCR3Configs[pluginType].Transmitters { - if !bytes.Equal(transmitter.Bytes(), ocrConfig.Transmitters[i].Bytes()) { - return fmt.Errorf("%s OCR3 config transmitter mismatch", pluginType.String()) - } - } - } - - return nil -} - -func ApplyChainConfigUpdatesOp( - e deployment.Environment, - state CCIPOnChainState, - homeChainSel uint64, - chains []uint64, -) (mcms.Operation, error) { - nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) - if err != nil { - return mcms.Operation{}, err - } - encodedExtraChainConfig, err := chainconfig.EncodeChainConfig(chainconfig.ChainConfig{ - GasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(1000), - DAGasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(0), - OptimisticConfirmations: 1, - }) - if err != nil { - return mcms.Operation{}, err - } - var chainConfigUpdates []ccip_home.CCIPHomeChainConfigArgs - for _, chainSel := range chains { - chainConfig := SetupConfigInfo(chainSel, nodes.NonBootstraps().PeerIDs(), - nodes.DefaultF(), encodedExtraChainConfig) - chainConfigUpdates = append(chainConfigUpdates, chainConfig) - } - - addChain, err := state.Chains[homeChainSel].CCIPHome.ApplyChainConfigUpdates( - deployment.SimTransactOpts(), - nil, - chainConfigUpdates, - ) - if err != nil { - return mcms.Operation{}, err - } - return mcms.Operation{ - To: state.Chains[homeChainSel].CCIPHome.Address(), - Data: addChain.Data(), - Value: big.NewInt(0), - }, nil -} diff --git a/deployment/ccip/changeset/jobs.go b/deployment/ccip/changeset/jobs.go deleted file mode 100644 index b7ffed45cac..00000000000 --- a/deployment/ccip/changeset/jobs.go +++ /dev/null @@ -1,57 +0,0 @@ -package ccipdeployment - -import ( - "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/validate" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" -) - -// In our case, the only address needed is the cap registry which is actually an env var. -// and will pre-exist for our deployment. So the job specs only depend on the environment operators. -func NewCCIPJobSpecs(nodeIds []string, oc deployment.OffchainClient) (map[string][]string, error) { - nodes, err := deployment.NodeInfo(nodeIds, oc) - if err != nil { - return nil, err - } - // Generate a set of brand new job specs for CCIP for a specific environment - // (including NOPs) and new addresses. - // We want to assign one CCIP capability job to each node. And node with - // an addr we'll list as bootstrapper. - // Find the bootstrap nodes - - nodesToJobSpecs := make(map[string][]string) - for _, node := range nodes { - var spec string - var err error - if !node.IsBootstrap { - spec, err = validate.NewCCIPSpecToml(validate.SpecArgs{ - P2PV2Bootstrappers: nodes.BootstrapLocators(), - CapabilityVersion: CapabilityVersion, - CapabilityLabelledName: CapabilityLabelledName, - OCRKeyBundleIDs: map[string]string{ - // TODO: Validate that that all EVM chains are using the same keybundle. - relay.NetworkEVM: node.FirstOCRKeybundle().KeyBundleID, - }, - P2PKeyID: node.PeerID.String(), - RelayConfigs: nil, - PluginConfig: map[string]any{}, - }) - } else { - spec, err = validate.NewCCIPSpecToml(validate.SpecArgs{ - P2PV2Bootstrappers: []string{}, // Intentionally empty for bootstraps. - CapabilityVersion: CapabilityVersion, - CapabilityLabelledName: CapabilityLabelledName, - OCRKeyBundleIDs: map[string]string{}, - // TODO: validate that all EVM chains are using the same keybundle - P2PKeyID: node.PeerID.String(), - RelayConfigs: nil, - PluginConfig: map[string]any{}, - }) - } - if err != nil { - return nil, err - } - nodesToJobSpecs[node.NodeID] = append(nodesToJobSpecs[node.NodeID], spec) - } - return nodesToJobSpecs, nil -} From dc9acfa8cf5eb06787bc9983b11e810afc742a72 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 18 Nov 2024 15:11:36 -0500 Subject: [PATCH 07/13] Extract common integration helper code --- .../ccip-tests/testsetups/test_helpers.go | 114 +++++++++++++++--- .../smoke/ccip_messaging_test.go | 54 --------- integration-tests/smoke/ccip_test.go | 110 +---------------- integration-tests/smoke/ccip_usdc_test.go | 98 +-------------- integration-tests/smoke/fee_boosting_test.go | 42 ------- 5 files changed, 100 insertions(+), 318 deletions(-) diff --git a/integration-tests/ccip-tests/testsetups/test_helpers.go b/integration-tests/ccip-tests/testsetups/test_helpers.go index 246584be7d5..a0d90356f71 100644 --- a/integration-tests/ccip-tests/testsetups/test_helpers.go +++ b/integration-tests/ccip-tests/testsetups/test_helpers.go @@ -3,12 +3,17 @@ package testsetups import ( "fmt" "math/big" + "net/http" + "net/http/httptest" "os" "strconv" "testing" + "time" chainsel "github.com/smartcontractkit/chain-selectors" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" ctfconfig "github.com/smartcontractkit/chainlink-testing-framework/lib/config" ctftestenv "github.com/smartcontractkit/chainlink-testing-framework/lib/docker/test_env" @@ -18,12 +23,16 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" "github.com/smartcontractkit/chainlink-testing-framework/seth" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment" ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" "github.com/smartcontractkit/chainlink/deployment/environment/devenv" clclient "github.com/smartcontractkit/chainlink/deployment/environment/nodeclient" "github.com/smartcontractkit/chainlink/integration-tests/actions" + ccipactions "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/testconfig" @@ -75,6 +84,25 @@ func NewLocalDevEnvironmentWithDefaultPrice( return NewLocalDevEnvironment(t, lggr, ccipdeployment.MockLinkPrice, ccipdeployment.MockWethPrice) } +// mockAttestationResponse mocks the USDC attestation server, it returns random Attestation. +// We don't need to return exactly the same attestation, because our Mocked USDC contract doesn't rely on any specific +// value, but instead of that it just checks if the attestation is present. Therefore, it makes the test a bit simpler +// and doesn't require very detailed mocks. Please see tests in chainlink-ccip for detailed tests using real attestations +func mockAttestationResponse() *httptest.Server { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + response := `{ + "status": "complete", + "attestation": "0x9049623e91719ef2aa63c55f357be2529b0e7122ae552c18aff8db58b4633c4d3920ff03d3a6d1ddf11f06bf64d7fd60d45447ac81f527ba628877dc5ca759651b08ffae25a6d3b1411749765244f0a1c131cbfe04430d687a2e12fd9d2e6dc08e118ad95d94ad832332cf3c4f7a4f3da0baa803b7be024b02db81951c0f0714de1b" + }` + + _, err := w.Write([]byte(response)) + if err != nil { + panic(err) + } + })) + return server +} + func NewLocalDevEnvironment( t *testing.T, lggr logger.Logger, @@ -125,6 +153,76 @@ func NewLocalDevEnvironment( // fund the nodes FundNodes(t, zeroLogLggr, testEnv, cfg, don.PluginNodes()) + output, err := changeset.DeployPrerequisites(*e, changeset.DeployPrerequisiteConfig{ + ChainSelectors: e.AllChainSelectors(), + }) + require.NoError(t, err) + require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) + mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) + for _, chain := range e.AllChainSelectors() { + mcmsCfg[chain] = commontypes.MCMSWithTimelockConfig{ + Canceller: commonchangeset.SingleGroupMCMS(t), + Bypasser: commonchangeset.SingleGroupMCMS(t), + Proposer: commonchangeset.SingleGroupMCMS(t), + TimelockExecutors: e.AllDeployerKeys(), + TimelockMinDelay: big.NewInt(0), + } + } + output, err = commonchangeset.DeployMCMSWithTimelock(*e, mcmsCfg) + require.NoError(t, err) + require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) + + state, err := ccipdeployment.LoadOnchainState(*e) + require.NoError(t, err) + + var endpoint string + // When inmemory env then spin up in memory mock server + if testEnv == nil { + server := mockAttestationResponse() + defer server.Close() + endpoint = server.URL + } else { + err := ccipactions.SetMockServerWithUSDCAttestation(e.MockAdapter, nil) + require.NoError(t, err) + endpoint = e.MockAdapter.InternalEndpoint + } + + tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[feedSel].USDFeeds) + // Apply migration + output, err = changeset.InitialDeploy(*e, ccipdeployment.DeployCCIPContractConfig{ + HomeChainSel: homeChainSel, + FeedChainSel: feedSel, + ChainsToDeploy: e.AllChainSelectors(), + TokenConfig: tokenConfig, + OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + USDCConfig: ccipdeployment.USDCConfig{ + Enabled: true, + USDCAttestationConfig: ccipdeployment.USDCAttestationConfig{ + API: endpoint, + APITimeout: commonconfig.MustNewDuration(time.Second), + APIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), + }, + }, + }) + require.NoError(t, err) + require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) + + // Ensure capreg logs are up to date. + ccipdeployment.ReplayLogs(t, e.Offchain, 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) + } + } + return ccipdeployment.DeployedEnv{ Env: *e, HomeChainSel: homeChainSel, @@ -139,22 +237,6 @@ func NewLocalDevEnvironmentWithRMN( numRmnNodes int, ) (ccipdeployment.DeployedEnv, devenv.RMNCluster) { tenv, dockerenv, _ := NewLocalDevEnvironmentWithDefaultPrice(t, lggr) - state, err := ccipdeployment.LoadOnchainState(tenv.Env) - require.NoError(t, err) - - // Deploy CCIP contracts. - newAddresses := deployment.NewMemoryAddressBook() - err = ccipdeployment.DeployCCIPContracts(tenv.Env, newAddresses, ccipdeployment.DeployCCIPContractConfig{ - HomeChainSel: tenv.HomeChainSel, - FeedChainSel: tenv.FeedChainSel, - ChainsToDeploy: tenv.Env.AllChainSelectors(), - TokenConfig: ccipdeployment.NewTestTokenConfig(state.Chains[tenv.FeedChainSel].USDFeeds), - MCMSConfig: ccipdeployment.NewTestMCMSConfig(t, tenv.Env), - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - }) - require.NoError(t, err) - require.NoError(t, tenv.Env.ExistingAddresses.Merge(newAddresses)) - l := logging.GetTestLogger(t) config := GenerateTestRMNConfig(t, numRmnNodes, tenv, MustNetworksToRPCMap(dockerenv.EVMNetworks)) rmnCluster, err := devenv.NewRMNCluster( diff --git a/integration-tests/smoke/ccip_messaging_test.go b/integration-tests/smoke/ccip_messaging_test.go index cbe5f7b8ba1..6bb34658e22 100644 --- a/integration-tests/smoke/ccip_messaging_test.go +++ b/integration-tests/smoke/ccip_messaging_test.go @@ -15,13 +15,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/hashutil" "github.com/smartcontractkit/chainlink-common/pkg/merklemulti" "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" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" - commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testsetups" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" @@ -68,55 +63,6 @@ func Test_CCIPMessaging(t *testing.T) { ", source chain selector:", sourceChain, ", dest chain selector:", destChain, ) - output, err := changeset.DeployPrerequisites(e.Env, changeset.DeployPrerequisiteConfig{ - ChainSelectors: e.Env.AllChainSelectors(), - }) - require.NoError(t, err) - require.NoError(t, e.Env.ExistingAddresses.Merge(output.AddressBook)) - - cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) - for _, chain := range allChainSelectors { - cfg[chain] = commontypes.MCMSWithTimelockConfig{ - Canceller: commonchangeset.SingleGroupMCMS(t), - Bypasser: commonchangeset.SingleGroupMCMS(t), - Proposer: commonchangeset.SingleGroupMCMS(t), - TimelockExecutors: e.Env.AllDeployerKeys(), - TimelockMinDelay: big.NewInt(0), - } - } - output, err = commonchangeset.DeployMCMSWithTimelock(e.Env, cfg) - require.NoError(t, err) - require.NoError(t, e.Env.ExistingAddresses.Merge(output.AddressBook)) - tokenConfig := ccdeploy.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) - output, err = changeset.InitialDeploy(e.Env, ccdeploy.DeployCCIPContractConfig{ - HomeChainSel: e.HomeChainSel, - FeedChainSel: e.FeedChainSel, - ChainsToDeploy: allChainSelectors, - TokenConfig: tokenConfig, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - }) - require.NoError(t, err) - 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, ccdeploy.AddLaneWithDefaultPrices(e.Env, state, sourceChain, destChain)) diff --git a/integration-tests/smoke/ccip_test.go b/integration-tests/smoke/ccip_test.go index 3e7092cc937..36aed7d5baa 100644 --- a/integration-tests/smoke/ccip_test.go +++ b/integration-tests/smoke/ccip_test.go @@ -7,14 +7,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" - commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" - commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" - - "github.com/smartcontractkit/chainlink/deployment" ccdeploy "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" @@ -23,63 +17,11 @@ import ( func TestInitialDeployOnLocal(t *testing.T) { t.Parallel() lggr := logger.TestLogger(t) - ctx := ccdeploy.Context(t) tenv, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr) e := tenv.Env - - state, err := ccdeploy.LoadOnchainState(tenv.Env) - require.NoError(t, err) - - feeds := state.Chains[tenv.FeedChainSel].USDFeeds - output, err := changeset.DeployPrerequisites(tenv.Env, changeset.DeployPrerequisiteConfig{ - ChainSelectors: tenv.Env.AllChainSelectors(), - }) - require.NoError(t, err) - require.NoError(t, tenv.Env.ExistingAddresses.Merge(output.AddressBook)) - - cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) - for _, chain := range e.AllChainSelectors() { - cfg[chain] = commontypes.MCMSWithTimelockConfig{ - Canceller: commonchangeset.SingleGroupMCMS(t), - Bypasser: commonchangeset.SingleGroupMCMS(t), - Proposer: commonchangeset.SingleGroupMCMS(t), - TimelockExecutors: e.AllDeployerKeys(), - TimelockMinDelay: big.NewInt(0), - } - } - output, err = commonchangeset.DeployMCMSWithTimelock(e, cfg) - require.NoError(t, err) - require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) - - output, err = changeset.InitialDeploy(tenv.Env, ccdeploy.DeployCCIPContractConfig{ - HomeChainSel: tenv.HomeChainSel, - FeedChainSel: tenv.FeedChainSel, - ChainsToDeploy: tenv.Env.AllChainSelectors(), - TokenConfig: ccdeploy.NewTestTokenConfig(feeds), - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - }) - require.NoError(t, err) - require.NoError(t, tenv.Env.ExistingAddresses.Merge(output.AddressBook)) - // Get new state after migration. - state, err = ccdeploy.LoadOnchainState(e) + state, err := ccdeploy.LoadOnchainState(e) require.NoError(t, err) - // 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. @@ -127,45 +69,11 @@ func TestInitialDeployOnLocal(t *testing.T) { func TestTokenTransfer(t *testing.T) { t.Parallel() lggr := logger.TestLogger(t) - ctx := ccdeploy.Context(t) tenv, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr) - e := tenv.Env state, err := ccdeploy.LoadOnchainState(e) require.NoError(t, err) - output, err := changeset.DeployPrerequisites(e, changeset.DeployPrerequisiteConfig{ - ChainSelectors: e.AllChainSelectors(), - }) - require.NoError(t, err) - require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) - - cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) - for _, chain := range e.AllChainSelectors() { - cfg[chain] = commontypes.MCMSWithTimelockConfig{ - Canceller: commonchangeset.SingleGroupMCMS(t), - Bypasser: commonchangeset.SingleGroupMCMS(t), - Proposer: commonchangeset.SingleGroupMCMS(t), - TimelockExecutors: e.AllDeployerKeys(), - TimelockMinDelay: big.NewInt(0), - } - } - output, err = commonchangeset.DeployMCMSWithTimelock(e, cfg) - require.NoError(t, err) - require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) - output, err = changeset.InitialDeploy(e, ccdeploy.DeployCCIPContractConfig{ - HomeChainSel: tenv.HomeChainSel, - FeedChainSel: tenv.FeedChainSel, - ChainsToDeploy: e.AllChainSelectors(), - TokenConfig: ccdeploy.NewTestTokenConfig(state.Chains[tenv.FeedChainSel].USDFeeds), - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - }) - require.NoError(t, err) - require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) - // Get new state after migration and mock USDC token deployment. - state, err = ccdeploy.LoadOnchainState(e) - require.NoError(t, err) - srcToken, _, dstToken, _, err := ccdeploy.DeployTransferableToken( lggr, tenv.Env.Chains, @@ -177,22 +85,6 @@ func TestTokenTransfer(t *testing.T) { ) require.NoError(t, err) - // 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. diff --git a/integration-tests/smoke/ccip_usdc_test.go b/integration-tests/smoke/ccip_usdc_test.go index 9fb4544d9f6..541ab2dda76 100644 --- a/integration-tests/smoke/ccip_usdc_test.go +++ b/integration-tests/smoke/ccip_usdc_test.go @@ -2,10 +2,7 @@ package smoke import ( "math/big" - "net/http" - "net/http/httptest" "testing" - "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -13,16 +10,10 @@ import ( "golang.org/x/exp/maps" - cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" - "github.com/smartcontractkit/chainlink-ccip/pluginconfig" - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" "github.com/smartcontractkit/chainlink/deployment" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testsetups" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -33,20 +24,7 @@ import ( func TestUSDCTokenTransfer(t *testing.T) { lggr := logger.TestLogger(t) - ctx := ccdeploy.Context(t) - tenv, cluster, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr) - - var endpoint string - // When inmemory env then spin up in memory mock server - if cluster == nil { - server := mockAttestationResponse() - defer server.Close() - endpoint = server.URL - } else { - err := actions.SetMockServerWithUSDCAttestation(tenv.Env.MockAdapter, nil) - require.NoError(t, err) - endpoint = tenv.Env.MockAdapter.InternalEndpoint - } + tenv, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr) e := tenv.Env state, err := ccdeploy.LoadOnchainState(e) @@ -56,64 +34,9 @@ func TestUSDCTokenTransfer(t *testing.T) { sourceChain := allChainSelectors[0] destChain := allChainSelectors[1] - 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), - }, - ) - - output, err := changeset.DeployPrerequisites(e, changeset.DeployPrerequisiteConfig{ - ChainSelectors: e.AllChainSelectors(), - }) - require.NoError(t, err) - require.NoError(t, tenv.Env.ExistingAddresses.Merge(output.AddressBook)) - - // Apply migration - output, err = changeset.InitialDeploy(e, ccdeploy.DeployCCIPContractConfig{ - HomeChainSel: tenv.HomeChainSel, - FeedChainSel: tenv.FeedChainSel, - ChainsToDeploy: e.AllChainSelectors(), - TokenConfig: tokenConfig, - MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e), - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - USDCConfig: ccdeploy.USDCConfig{ - Enabled: true, - USDCAttestationConfig: ccdeploy.USDCAttestationConfig{ - API: endpoint, - APITimeout: commonconfig.MustNewDuration(time.Second), - APIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), - }, - }, - }) - require.NoError(t, err) - require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) - - state, err = ccdeploy.LoadOnchainState(e) - require.NoError(t, err) - srcUSDC, dstUSDC, err := ccdeploy.ConfigureUSDCTokenPools(lggr, e.Chains, sourceChain, destChain, state) require.NoError(t, err) - // 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)) @@ -262,22 +185,3 @@ func transferAndWaitForSuccess( // Wait for all exec reports to land ccdeploy.ConfirmExecWithSeqNrForAll(t, env, state, expectedSeqNum, startBlocks) } - -// mockAttestationResponse mocks the USDC attestation server, it returns random Attestation. -// We don't need to return exactly the same attestation, because our Mocked USDC contract doesn't rely on any specific -// value, but instead of that it just checks if the attestation is present. Therefore, it makes the test a bit simpler -// and doesn't require very detailed mocks. Please see tests in chainlink-ccip for detailed tests using real attestations -func mockAttestationResponse() *httptest.Server { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - response := `{ - "status": "complete", - "attestation": "0x9049623e91719ef2aa63c55f357be2529b0e7122ae552c18aff8db58b4633c4d3920ff03d3a6d1ddf11f06bf64d7fd60d45447ac81f527ba628877dc5ca759651b08ffae25a6d3b1411749765244f0a1c131cbfe04430d687a2e12fd9d2e6dc08e118ad95d94ad832332cf3c4f7a4f3da0baa803b7be024b02db81951c0f0714de1b" - }` - - _, err := w.Write([]byte(response)) - if err != nil { - panic(err) - } - })) - return server -} diff --git a/integration-tests/smoke/fee_boosting_test.go b/integration-tests/smoke/fee_boosting_test.go index 625200360e8..0e0fb094016 100644 --- a/integration-tests/smoke/fee_boosting_test.go +++ b/integration-tests/smoke/fee_boosting_test.go @@ -9,10 +9,8 @@ import ( "github.com/test-go/testify/require" "golang.org/x/exp/maps" - jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink/deployment" ccdeploy "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" @@ -35,8 +33,6 @@ type priceFeedPrices struct { // TODO: find a way to reuse the same test setup for all tests func Test_CCIPFeeBoosting(t *testing.T) { - ctx := ccdeploy.Context(t) - setupTestEnv := func(t *testing.T, numChains int) (ccdeploy.DeployedEnv, ccdeploy.CCIPOnChainState, []uint64) { e, _, _ := testsetups.NewLocalDevEnvironment( t, logger.TestLogger(t), @@ -48,44 +44,6 @@ func Test_CCIPFeeBoosting(t *testing.T) { allChainSelectors := maps.Keys(e.Env.Chains) require.Len(t, allChainSelectors, numChains) - - output, err := changeset.DeployPrerequisites(e.Env, changeset.DeployPrerequisiteConfig{ - ChainSelectors: e.Env.AllChainSelectors(), - }) - require.NoError(t, err) - require.NoError(t, e.Env.ExistingAddresses.Merge(output.AddressBook)) - - tokenConfig := ccdeploy.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) - // Apply migration - output, err = changeset.InitialDeploy(e.Env, ccdeploy.DeployCCIPContractConfig{ - HomeChainSel: e.HomeChainSel, - FeedChainSel: e.FeedChainSel, - ChainsToDeploy: allChainSelectors, - TokenConfig: tokenConfig, - MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e.Env), - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - }) - require.NoError(t, err) - require.NoError(t, e.Env.ExistingAddresses.Merge(output.AddressBook)) - 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) - } - } - return e, state, allChainSelectors } From 727b9e451673a8bb4822c59d2fb027d486a97f6d Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 18 Nov 2024 15:17:57 -0500 Subject: [PATCH 08/13] Fix go mod --- integration-tests/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 4bf6361f27b..ba17c786fc0 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -36,7 +36,6 @@ require ( github.com/slack-go/slack v0.15.0 github.com/smartcontractkit/chain-selectors v1.0.29 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241118091009-43c2b4804cec github.com/smartcontractkit/chainlink-common v0.3.1-0.20241114134822-aadff98ef068 github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 @@ -414,6 +413,7 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241118091009-43c2b4804cec // 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 From edf7c1ad99788ef7a6ce90064365f0aa14be5239 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 18 Nov 2024 15:36:44 -0500 Subject: [PATCH 09/13] Similar common helper for memeroy --- deployment/ccip/add_lane_test.go | 24 +--- .../ccip/changeset/active_candidate_test.go | 103 ++++++------------ deployment/ccip/deploy_test.go | 51 +-------- deployment/ccip/test_helpers.go | 83 +++++++++++++- .../ccip-tests/testsetups/test_helpers.go | 35 +----- 5 files changed, 124 insertions(+), 172 deletions(-) diff --git a/deployment/ccip/add_lane_test.go b/deployment/ccip/add_lane_test.go index 15036476647..5c87a089a1b 100644 --- a/deployment/ccip/add_lane_test.go +++ b/deployment/ccip/add_lane_test.go @@ -21,37 +21,15 @@ import ( func TestAddLane(t *testing.T) { t.Parallel() // We add more chains to the chainlink nodes than the number of chains where CCIP is deployed. - e := NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 4, 4) + e := NewMemoryEnvironmentWithJobsAndContracts(t, logger.TestLogger(t), 2, 4) // Here we have CR + nodes set up, but no CCIP contracts deployed. state, err := LoadOnchainState(e.Env) require.NoError(t, err) selectors := e.Env.AllChainSelectors() - // deploy CCIP contracts on two chains chain1, chain2 := selectors[0], selectors[1] - feeds := state.Chains[e.FeedChainSel].USDFeeds - tokenConfig := NewTestTokenConfig(feeds) - newAddresses := deployment.NewMemoryAddressBook() - err = DeployPrerequisiteChainContracts(e.Env, newAddresses, e.Env.AllChainSelectors()) - require.NoError(t, err) - require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) - - // Set up CCIP contracts and a DON per chain. - newAddresses = deployment.NewMemoryAddressBook() - err = DeployCCIPContracts(e.Env, newAddresses, DeployCCIPContractConfig{ - HomeChainSel: e.HomeChainSel, - FeedChainSel: e.FeedChainSel, - TokenConfig: tokenConfig, - ChainsToDeploy: []uint64{chain1, chain2}, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - }) - require.NoError(t, err) - require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) - // We expect no lanes available on any chain. - state, err = LoadOnchainState(e.Env) - require.NoError(t, err) for _, sel := range []uint64{chain1, chain2} { chain := state.Chains[sel] offRamps, err := chain.Router.GetOffRamps(nil) diff --git a/deployment/ccip/changeset/active_candidate_test.go b/deployment/ccip/changeset/active_candidate_test.go index 61afce232fe..50115389a28 100644 --- a/deployment/ccip/changeset/active_candidate_test.go +++ b/deployment/ccip/changeset/active_candidate_test.go @@ -16,8 +16,6 @@ import ( "github.com/stretchr/testify/require" - jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" - ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" @@ -28,46 +26,10 @@ func TestActiveCandidate(t *testing.T) { t.Skipf("to be enabled after latest cl-ccip is compatible") lggr := logger.TestLogger(t) - ctx := ccdeploy.Context(t) - tenv := ccdeploy.NewMemoryEnvironment(t, lggr, 3, 5, ccdeploy.MockLinkPrice, ccdeploy.MockWethPrice) + tenv := ccdeploy.NewMemoryEnvironmentWithJobsAndContracts(t, lggr, 3, 5) e := tenv.Env - state, err := ccdeploy.LoadOnchainState(tenv.Env) require.NoError(t, err) - require.NotNil(t, state.Chains[tenv.HomeChainSel].LinkToken) - - feeds := state.Chains[tenv.FeedChainSel].USDFeeds - tokenConfig := ccdeploy.NewTestTokenConfig(feeds) - - output, err := InitialDeploy(tenv.Env, ccdeploy.DeployCCIPContractConfig{ - HomeChainSel: tenv.HomeChainSel, - FeedChainSel: tenv.FeedChainSel, - ChainsToDeploy: tenv.Env.AllChainSelectors(), - TokenConfig: tokenConfig, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - }) - require.NoError(t, err) - // Get new state after migration. - require.NoError(t, tenv.Env.ExistingAddresses.Merge(output.AddressBook)) - state, err = ccdeploy.LoadOnchainState(tenv.Env) - 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)) @@ -111,8 +73,8 @@ func TestActiveCandidate(t *testing.T) { ccdeploy.ConfirmExecWithSeqNrForAll(t, e, state, expectedSeqNum, startBlocks) // transfer ownership - ccdeploy.TransferAllOwnership(t, state, homeCS, e) - acceptOwnershipProposal, err := ccdeploy.GenerateAcceptOwnershipProposal(state, homeCS, e.AllChainSelectors()) + ccdeploy.TransferAllOwnership(t, state, tenv.HomeChainSel, e) + acceptOwnershipProposal, err := ccdeploy.GenerateAcceptOwnershipProposal(state, tenv.HomeChainSel, e.AllChainSelectors()) require.NoError(t, err) acceptOwnershipExec := commonchangeset.SignProposal(t, e, acceptOwnershipProposal) for _, sel := range e.AllChainSelectors() { @@ -120,14 +82,14 @@ func TestActiveCandidate(t *testing.T) { } // Apply the accept ownership proposal to all the chains. - err = ccdeploy.ConfirmRequestOnSourceAndDest(t, e, state, homeCS, destCS, 2) + err = ccdeploy.ConfirmRequestOnSourceAndDest(t, e, state, tenv.HomeChainSel, tenv.FeedChainSel, 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) + capReg, ccipHome := state.Chains[tenv.HomeChainSel].CapabilityRegistry, state.Chains[tenv.HomeChainSel].CCIPHome + donID, err := ccdeploy.DonIDForChain(capReg, ccipHome, tenv.FeedChainSel) require.NoError(t, err) - donInfo, err := state.Chains[homeCS].CapabilityRegistry.GetDON(nil, donID) + donInfo, err := state.Chains[tenv.HomeChainSel].CapabilityRegistry.GetDON(nil, donID) require.NoError(t, err) require.Equal(t, 5, len(donInfo.NodeP2PIds)) require.Equal(t, uint32(4), donInfo.ConfigCount) @@ -151,13 +113,14 @@ func TestActiveCandidate(t *testing.T) { // this will construct ocr3 configurations for the // commit and exec plugin we will be using - rmnHomeAddress := state.Chains[homeCS].RMNHome.Address() + rmnHomeAddress := state.Chains[tenv.HomeChainSel].RMNHome.Address() + tokenConfig := ccdeploy.NewTestTokenConfig(state.Chains[tenv.FeedChainSel].USDFeeds) ocr3ConfigMap, err := ccdeploy.BuildOCR3ConfigForCCIPHome( deployment.XXXGenerateTestOCRSecrets(), - state.Chains[destCS].OffRamp, - e.Chains[destCS], - destCS, - tokenConfig.GetTokenInfo(e.Logger, state.Chains[destCS].LinkToken, state.Chains[destCS].Weth9), + state.Chains[tenv.FeedChainSel].OffRamp, + e.Chains[tenv.FeedChainSel], + tenv.FeedChainSel, + tokenConfig.GetTokenInfo(e.Logger, state.Chains[tenv.FeedChainSel].LinkToken, state.Chains[tenv.FeedChainSel].Weth9), nodes.NonBootstraps(), rmnHomeAddress, nil, @@ -166,82 +129,82 @@ func TestActiveCandidate(t *testing.T) { setCommitCandidateOp, err := ccdeploy.SetCandidateOnExistingDon( ocr3ConfigMap[cctypes.PluginTypeCCIPCommit], - state.Chains[homeCS].CapabilityRegistry, - state.Chains[homeCS].CCIPHome, - destCS, + state.Chains[tenv.HomeChainSel].CapabilityRegistry, + state.Chains[tenv.HomeChainSel].CCIPHome, + tenv.FeedChainSel, nodes.NonBootstraps(), ) require.NoError(t, err) setCommitCandidateProposal, err := ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ - ChainIdentifier: mcms.ChainIdentifier(homeCS), + ChainIdentifier: mcms.ChainIdentifier(tenv.HomeChainSel), Batch: setCommitCandidateOp, }}, "set new candidates on commit plugin", 0) require.NoError(t, err) setCommitCandidateSigned := commonchangeset.SignProposal(t, e, setCommitCandidateProposal) - commonchangeset.ExecuteProposal(t, e, setCommitCandidateSigned, state.Chains[homeCS].Timelock, homeCS) + commonchangeset.ExecuteProposal(t, e, setCommitCandidateSigned, state.Chains[tenv.HomeChainSel].Timelock, tenv.HomeChainSel) // 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, + state.Chains[tenv.HomeChainSel].CapabilityRegistry, + state.Chains[tenv.HomeChainSel].CCIPHome, + tenv.FeedChainSel, nodes.NonBootstraps(), ) require.NoError(t, err) setExecCandidateProposal, err := ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ - ChainIdentifier: mcms.ChainIdentifier(homeCS), + ChainIdentifier: mcms.ChainIdentifier(tenv.HomeChainSel), Batch: setExecCandidateOp, }}, "set new candidates on commit and exec plugins", 0) require.NoError(t, err) setExecCandidateSigned := commonchangeset.SignProposal(t, e, setExecCandidateProposal) - commonchangeset.ExecuteProposal(t, e, setExecCandidateSigned, state.Chains[homeCS].Timelock, homeCS) + commonchangeset.ExecuteProposal(t, e, setExecCandidateSigned, state.Chains[tenv.HomeChainSel].Timelock, tenv.HomeChainSel) // check setup was successful by confirming number of nodes from cap reg - donInfo, err = state.Chains[homeCS].CapabilityRegistry.GetDON(nil, donID) + donInfo, err = state.Chains[tenv.HomeChainSel].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) + err = ccdeploy.ConfirmRequestOnSourceAndDest(t, e, state, tenv.HomeChainSel, tenv.FeedChainSel, 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)) + oldCandidateDigest, err := state.Chains[tenv.HomeChainSel].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()) + promoteOps, err := ccdeploy.PromoteAllCandidatesForChainOps(state.Chains[tenv.HomeChainSel].CapabilityRegistry, state.Chains[tenv.HomeChainSel].CCIPHome, tenv.FeedChainSel, nodes.NonBootstraps()) require.NoError(t, err) promoteProposal, err := ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ - ChainIdentifier: mcms.ChainIdentifier(homeCS), + ChainIdentifier: mcms.ChainIdentifier(tenv.HomeChainSel), Batch: promoteOps, }}, "promote candidates and revoke actives", 0) require.NoError(t, err) promoteSigned := commonchangeset.SignProposal(t, e, promoteProposal) - commonchangeset.ExecuteProposal(t, e, promoteSigned, state.Chains[homeCS].Timelock, homeCS) + commonchangeset.ExecuteProposal(t, e, promoteSigned, state.Chains[tenv.HomeChainSel].Timelock, tenv.HomeChainSel) // [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)) + newActiveDigest, err := state.Chains[tenv.HomeChainSel].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)) + newCandidateDigest, err := state.Chains[tenv.HomeChainSel].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) + donInfo, err = state.Chains[tenv.HomeChainSel].CapabilityRegistry.GetDON(nil, donID) require.NoError(t, err) require.Equal(t, uint32(8), donInfo.ConfigCount) - err = ccdeploy.ConfirmRequestOnSourceAndDest(t, e, state, homeCS, destCS, 4) + err = ccdeploy.ConfirmRequestOnSourceAndDest(t, e, state, tenv.HomeChainSel, tenv.FeedChainSel, 4) require.NoError(t, err) // [NEW ACTIVE, NO CANDIDATE] done sending successful request } diff --git a/deployment/ccip/deploy_test.go b/deployment/ccip/deploy_test.go index 5e5efb289bc..424077827b3 100644 --- a/deployment/ccip/deploy_test.go +++ b/deployment/ccip/deploy_test.go @@ -8,59 +8,20 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/logger" ) func TestDeployCCIPContracts(t *testing.T) { lggr := logger.TestLogger(t) - e := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ - Bootstraps: 1, - Chains: 2, - Nodes: 4, - }) - // Deploy all the CCIP contracts. - homeChainSel, feedChainSel := allocateCCIPChainSelectors(e.Chains) - _ = DeployTestContracts(t, lggr, e.ExistingAddresses, homeChainSel, feedChainSel, e.Chains, MockLinkPrice, MockWethPrice) - - nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) - require.NoError(t, err) - - _, err = DeployHomeChain(lggr, e, e.ExistingAddresses, e.Chains[homeChainSel], - NewTestRMNStaticConfig(), - NewTestRMNDynamicConfig(), - NewTestNodeOperator(e.Chains[homeChainSel].DeployerKey.From), - map[string][][32]byte{ - "NodeOperator": nodes.NonBootstraps().PeerIDs(), - }, + e := NewMemoryEnvironmentWithJobsAndContracts(t, lggr, + 2, + 4, ) + // Deploy all the CCIP contracts. + state, err := LoadOnchainState(e.Env) require.NoError(t, err) - // Load the state after deploying the cap reg and feeds. - s, err := LoadOnchainState(e) - require.NoError(t, err) - require.NotNil(t, s.Chains[homeChainSel].CapabilityRegistry) - require.NotNil(t, s.Chains[homeChainSel].CCIPHome) - require.NotNil(t, s.Chains[feedChainSel].USDFeeds) - - newAddresses := deployment.NewMemoryAddressBook() - err = DeployPrerequisiteChainContracts(e, newAddresses, e.AllChainSelectors()) - require.NoError(t, err) - require.NoError(t, e.ExistingAddresses.Merge(newAddresses)) - - newAddresses = deployment.NewMemoryAddressBook() - err = DeployCCIPContracts(e, newAddresses, DeployCCIPContractConfig{ - HomeChainSel: homeChainSel, - FeedChainSel: feedChainSel, - ChainsToDeploy: e.AllChainSelectors(), - TokenConfig: NewTokenConfig(), - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - }) - require.NoError(t, err) - require.NoError(t, e.ExistingAddresses.Merge(newAddresses)) - state, err := LoadOnchainState(e) - require.NoError(t, err) - snap, err := state.View(e.AllChainSelectors()) + snap, err := state.View(e.Env.AllChainSelectors()) require.NoError(t, err) // Assert expect every deployed address to be in the address book. diff --git a/deployment/ccip/test_helpers.go b/deployment/ccip/test_helpers.go index 3ba6b2f294b..f858164e720 100644 --- a/deployment/ccip/test_helpers.go +++ b/deployment/ccip/test_helpers.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "math/big" + "net/http" + "net/http/httptest" "sort" "testing" "time" @@ -14,7 +16,9 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -219,6 +223,79 @@ func NewMemoryEnvironmentWithJobs(t *testing.T, lggr logger.Logger, numChains in return e } +// mockAttestationResponse mocks the USDC attestation server, it returns random Attestation. +// We don't need to return exactly the same attestation, because our Mocked USDC contract doesn't rely on any specific +// value, but instead of that it just checks if the attestation is present. Therefore, it makes the test a bit simpler +// and doesn't require very detailed mocks. Please see tests in chainlink-ccip for detailed tests using real attestations +func mockAttestationResponse() *httptest.Server { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + response := `{ + "status": "complete", + "attestation": "0x9049623e91719ef2aa63c55f357be2529b0e7122ae552c18aff8db58b4633c4d3920ff03d3a6d1ddf11f06bf64d7fd60d45447ac81f527ba628877dc5ca759651b08ffae25a6d3b1411749765244f0a1c131cbfe04430d687a2e12fd9d2e6dc08e118ad95d94ad832332cf3c4f7a4f3da0baa803b7be024b02db81951c0f0714de1b" + }` + + _, err := w.Write([]byte(response)) + if err != nil { + panic(err) + } + })) + return server +} + +func NewMemoryEnvironmentWithJobsAndContracts(t *testing.T, lggr logger.Logger, numChains int, numNodes int) DeployedEnv { + e := NewMemoryEnvironment(t, lggr, numChains, numNodes, MockLinkPrice, MockWethPrice) + e.SetupJobs(t) + // Take first non-home chain as the new chain. + newAddresses := deployment.NewMemoryAddressBook() + err := DeployPrerequisiteChainContracts(e.Env, newAddresses, e.Env.AllChainSelectors()) + require.NoError(t, err) + require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) + + cfg := commontypes.MCMSWithTimelockConfig{ + Canceller: commonchangeset.SingleGroupMCMS(t), + Bypasser: commonchangeset.SingleGroupMCMS(t), + Proposer: commonchangeset.SingleGroupMCMS(t), + TimelockExecutors: e.Env.AllDeployerKeys(), + TimelockMinDelay: big.NewInt(0), + } + mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) + for _, c := range e.Env.AllChainSelectors() { + mcmsCfg[c] = cfg + } + out, err := commonchangeset.DeployMCMSWithTimelock(e.Env, mcmsCfg) + require.NoError(t, err) + require.NoError(t, e.Env.ExistingAddresses.Merge(out.AddressBook)) + state, err := LoadOnchainState(e.Env) + require.NoError(t, err) + + newAddresses = deployment.NewMemoryAddressBook() + tokenConfig := NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) + server := mockAttestationResponse() + defer server.Close() + endpoint := server.URL + err = DeployCCIPContracts(e.Env, newAddresses, DeployCCIPContractConfig{ + HomeChainSel: e.HomeChainSel, + FeedChainSel: e.FeedChainSel, + ChainsToDeploy: e.Env.AllChainSelectors(), + TokenConfig: tokenConfig, + OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + USDCConfig: USDCConfig{ + Enabled: true, + USDCAttestationConfig: USDCAttestationConfig{ + API: endpoint, + APITimeout: commonconfig.MustNewDuration(time.Second), + APIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), + }, + }, + }) + require.NoError(t, err) + require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) + state, err = LoadOnchainState(e.Env) + require.NoError(t, err) + + return e +} + func NewMemoryEnvironmentWithJobsAndPrices( t *testing.T, lggr logger.Logger, @@ -520,9 +597,9 @@ func ProcessChangeset(t *testing.T, e deployment.Environment, c deployment.Chang chains.Add(uint64(op.ChainIdentifier)) } - signed := commoncs.SignProposal(t, e, &prop) + signed := commonchangeset.SignProposal(t, e, &prop) for _, sel := range chains.ToSlice() { - commoncs.ExecuteProposal(t, e, signed, state.Chains[sel].Timelock, sel) + commonchangeset.ExecuteProposal(t, e, signed, state.Chains[sel].Timelock, sel) } } } diff --git a/integration-tests/ccip-tests/testsetups/test_helpers.go b/integration-tests/ccip-tests/testsetups/test_helpers.go index a0d90356f71..fd9cfdb4a94 100644 --- a/integration-tests/ccip-tests/testsetups/test_helpers.go +++ b/integration-tests/ccip-tests/testsetups/test_helpers.go @@ -3,8 +3,6 @@ package testsetups import ( "fmt" "math/big" - "net/http" - "net/http/httptest" "os" "strconv" "testing" @@ -84,25 +82,6 @@ func NewLocalDevEnvironmentWithDefaultPrice( return NewLocalDevEnvironment(t, lggr, ccipdeployment.MockLinkPrice, ccipdeployment.MockWethPrice) } -// mockAttestationResponse mocks the USDC attestation server, it returns random Attestation. -// We don't need to return exactly the same attestation, because our Mocked USDC contract doesn't rely on any specific -// value, but instead of that it just checks if the attestation is present. Therefore, it makes the test a bit simpler -// and doesn't require very detailed mocks. Please see tests in chainlink-ccip for detailed tests using real attestations -func mockAttestationResponse() *httptest.Server { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - response := `{ - "status": "complete", - "attestation": "0x9049623e91719ef2aa63c55f357be2529b0e7122ae552c18aff8db58b4633c4d3920ff03d3a6d1ddf11f06bf64d7fd60d45447ac81f527ba628877dc5ca759651b08ffae25a6d3b1411749765244f0a1c131cbfe04430d687a2e12fd9d2e6dc08e118ad95d94ad832332cf3c4f7a4f3da0baa803b7be024b02db81951c0f0714de1b" - }` - - _, err := w.Write([]byte(response)) - if err != nil { - panic(err) - } - })) - return server -} - func NewLocalDevEnvironment( t *testing.T, lggr logger.Logger, @@ -136,6 +115,7 @@ func NewLocalDevEnvironment( require.NoError(t, err) require.NotNil(t, e) e.ExistingAddresses = ab + require.NotNil(t, testEnv.MockAdapter) e.MockAdapter = testEnv.MockAdapter envNodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) @@ -176,16 +156,9 @@ func NewLocalDevEnvironment( require.NoError(t, err) var endpoint string - // When inmemory env then spin up in memory mock server - if testEnv == nil { - server := mockAttestationResponse() - defer server.Close() - endpoint = server.URL - } else { - err := ccipactions.SetMockServerWithUSDCAttestation(e.MockAdapter, nil) - require.NoError(t, err) - endpoint = e.MockAdapter.InternalEndpoint - } + err = ccipactions.SetMockServerWithUSDCAttestation(e.MockAdapter, nil) + require.NoError(t, err) + endpoint = e.MockAdapter.InternalEndpoint tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[feedSel].USDFeeds) // Apply migration From f842de18aabe7f2a527509e9012953c335437bef Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 18 Nov 2024 17:07:25 -0500 Subject: [PATCH 10/13] Comments --- deployment/common/changeset/deploy_mcms_with_timelock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/common/changeset/deploy_mcms_with_timelock.go b/deployment/common/changeset/deploy_mcms_with_timelock.go index 686d3bae19b..c36e1f1575b 100644 --- a/deployment/common/changeset/deploy_mcms_with_timelock.go +++ b/deployment/common/changeset/deploy_mcms_with_timelock.go @@ -14,7 +14,7 @@ func DeployMCMSWithTimelock(e deployment.Environment, cfgByChain map[uint64]type e.Logger, e.Chains, newAddresses, cfgByChain, ) if err != nil { - return deployment.ChangesetOutput{}, err + return deployment.ChangesetOutput{AddressBook: newAddresses}, err } return deployment.ChangesetOutput{AddressBook: newAddresses}, nil } From be8bf616bdbff6af2f05f36608c556413c71e4ed Mon Sep 17 00:00:00 2001 From: connorwstein Date: Tue, 19 Nov 2024 15:17:27 -0500 Subject: [PATCH 11/13] Fix tests --- deployment/ccip/changeset/deploy_chain_test.go | 1 - deployment/ccip/deploy_test.go | 2 -- 2 files changed, 3 deletions(-) diff --git a/deployment/ccip/changeset/deploy_chain_test.go b/deployment/ccip/changeset/deploy_chain_test.go index b197c90eca5..56aa7a029e7 100644 --- a/deployment/ccip/changeset/deploy_chain_test.go +++ b/deployment/ccip/changeset/deploy_chain_test.go @@ -49,7 +49,6 @@ func TestDeployChainContractsChangeset(t *testing.T) { output, err = DeployChainContracts(e, DeployChainContractsConfig{ ChainSelectors: selectors, HomeChainSelector: homeChainSel, - MCMSCfg: ccdeploy.NewTestMCMSConfig(t, e), }) require.NoError(t, err) require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) diff --git a/deployment/ccip/deploy_test.go b/deployment/ccip/deploy_test.go index 8fa75b373f3..c2b71e093de 100644 --- a/deployment/ccip/deploy_test.go +++ b/deployment/ccip/deploy_test.go @@ -6,9 +6,7 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/logger" ) From 99bec3c8e9b58aedfbd41053f1ec6078af195c75 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Tue, 19 Nov 2024 16:20:51 -0500 Subject: [PATCH 12/13] Fix new test --- deployment/ccip/changeset/deploy_chain_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/deployment/ccip/changeset/deploy_chain_test.go b/deployment/ccip/changeset/deploy_chain_test.go index 56aa7a029e7..0e5b7a8d270 100644 --- a/deployment/ccip/changeset/deploy_chain_test.go +++ b/deployment/ccip/changeset/deploy_chain_test.go @@ -1,6 +1,7 @@ package changeset import ( + "math/big" "testing" "github.com/stretchr/testify/require" @@ -8,6 +9,8 @@ import ( "github.com/smartcontractkit/chainlink/deployment" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -45,6 +48,20 @@ func TestDeployChainContractsChangeset(t *testing.T) { require.NoError(t, err) require.NoError(t, e.ExistingAddresses.Merge(prerequisites.AddressBook)) + cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) + for _, chain := range e.AllChainSelectors() { + cfg[chain] = commontypes.MCMSWithTimelockConfig{ + Canceller: commonchangeset.SingleGroupMCMS(t), + Bypasser: commonchangeset.SingleGroupMCMS(t), + Proposer: commonchangeset.SingleGroupMCMS(t), + TimelockExecutors: e.AllDeployerKeys(), + TimelockMinDelay: big.NewInt(0), + } + } + output, err = commonchangeset.DeployMCMSWithTimelock(e, cfg) + require.NoError(t, err) + require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) + // deploy ccip chain contracts output, err = DeployChainContracts(e, DeployChainContractsConfig{ ChainSelectors: selectors, From 0df43b8ceed72f8be03d2bb11a9491975535db9d Mon Sep 17 00:00:00 2001 From: connorwstein Date: Tue, 19 Nov 2024 17:02:55 -0500 Subject: [PATCH 13/13] Fix RMN test --- integration-tests/smoke/ccip_rmn_test.go | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/integration-tests/smoke/ccip_rmn_test.go b/integration-tests/smoke/ccip_rmn_test.go index e8e81688239..d1cadabc06a 100644 --- a/integration-tests/smoke/ccip_rmn_test.go +++ b/integration-tests/smoke/ccip_rmn_test.go @@ -13,7 +13,6 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" - jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/osutil" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" @@ -327,24 +326,7 @@ func runRmnTestCase(t *testing.T, tc rmnTestCase) { } } - jobSpecs, err := ccipdeployment.NewCCIPJobSpecs(envWithRMN.Env.NodeIDs, envWithRMN.Env.Offchain) - require.NoError(t, err) - - ctx := ccipdeployment.Context(t) - ccipdeployment.ReplayLogs(t, envWithRMN.Env.Offchain, envWithRMN.ReplayBlocks) - - for nodeID, jobs := range jobSpecs { - for _, job := range jobs { - _, err := envWithRMN.Env.Offchain.ProposeJob(ctx, - &jobv1.ProposeJobRequest{ - NodeId: nodeID, - Spec: job, - }) - require.NoError(t, err) - } - } - // Add all lanes require.NoError(t, ccipdeployment.AddLanesForAll(envWithRMN.Env, onChainState))