Skip to content

Commit

Permalink
Extract MCMS to deployment/common (#15288)
Browse files Browse the repository at this point in the history
* Extract MCMS

* Use changeset

* Standardize package import names

* Fix mcms test imports

* Fix add lane

* Fix bad merge

* Extract common integration helper code

* Fix go mod

* Similar common helper for memeroy

* Comments

* Fix tests

* Fix new test

* Fix RMN test
  • Loading branch information
connorwstein authored Nov 19, 2024
1 parent e1c6ef9 commit c0b7a23
Show file tree
Hide file tree
Showing 28 changed files with 913 additions and 780 deletions.
25 changes: 1 addition & 24 deletions deployment/ccip/add_lane_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +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,
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)
Expand Down
115 changes: 39 additions & 76 deletions deployment/ccip/changeset/active_candidate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ 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"

"github.com/smartcontractkit/chainlink/v2/core/logger"
)
Expand All @@ -27,47 +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,
MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e),
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))
Expand Down Expand Up @@ -111,23 +73,23 @@ 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 := ccdeploy.SignProposal(t, e, acceptOwnershipProposal)
acceptOwnershipExec := commonchangeset.SignProposal(t, e, acceptOwnershipProposal)
for _, sel := range e.AllChainSelectors() {
ccdeploy.ExecuteProposal(t, e, acceptOwnershipExec, state, sel)
commonchangeset.ExecuteProposal(t, e, acceptOwnershipExec, state.Chains[sel].Timelock, sel)
}
// 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)
Expand All @@ -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,
Expand All @@ -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 := ccdeploy.SignProposal(t, e, setCommitCandidateProposal)
ccdeploy.ExecuteProposal(t, e, setCommitCandidateSigned, state, homeCS)
setCommitCandidateSigned := commonchangeset.SignProposal(t, e, setCommitCandidateProposal)
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 := ccdeploy.SignProposal(t, e, setExecCandidateProposal)
ccdeploy.ExecuteProposal(t, e, setExecCandidateSigned, state, homeCS)
setExecCandidateSigned := commonchangeset.SignProposal(t, e, setExecCandidateProposal)
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 := ccdeploy.SignProposal(t, e, promoteProposal)
ccdeploy.ExecuteProposal(t, e, promoteSigned, state, homeCS)
promoteSigned := commonchangeset.SignProposal(t, e, promoteProposal)
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
}
34 changes: 27 additions & 7 deletions deployment/ccip/changeset/add_chain_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package changeset

import (
"math/big"
"testing"
"time"

ccipdeployment "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/v2/core/capabilities/ccip/types"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -41,14 +43,27 @@ func TestAddChainInbound(t *testing.T) {
require.NoError(t, err)
require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses))

tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds)
cfg := commontypes.MCMSWithTimelockConfig{
Canceller: commonchangeset.SingleGroupMCMS(t),
Bypasser: commonchangeset.SingleGroupMCMS(t),
Proposer: commonchangeset.SingleGroupMCMS(t),
TimelockExecutors: e.Env.AllDeployerKeys(),
TimelockMinDelay: big.NewInt(0),
}
out, err := commonchangeset.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()
tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds)
err = ccipdeployment.DeployCCIPContracts(e.Env, newAddresses, ccipdeployment.DeployCCIPContractConfig{
HomeChainSel: e.HomeChainSel,
FeedChainSel: e.FeedChainSel,
ChainsToDeploy: initialDeploy,
TokenConfig: tokenConfig,
MCMSConfig: ccipdeployment.NewTestMCMSConfig(t, e.Env),
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
})
require.NoError(t, err)
Expand All @@ -72,14 +87,19 @@ func TestAddChainInbound(t *testing.T) {
require.NoError(t, err)

// Deploy contracts to new chain
out, err = commonchangeset.DeployMCMSWithTimelock(e.Env, map[uint64]commontypes.MCMSWithTimelockConfig{
newChain: cfg,
})
require.NoError(t, err)
require.NoError(t, e.Env.ExistingAddresses.Merge(out.AddressBook))

newAddresses = deployment.NewMemoryAddressBook()
err = ccipdeployment.DeployPrerequisiteChainContracts(e.Env, newAddresses, []uint64{newChain})
require.NoError(t, err)
require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses))
newAddresses = deployment.NewMemoryAddressBook()
err = ccipdeployment.DeployChainContracts(e.Env,
e.Env.Chains[newChain], newAddresses,
ccipdeployment.NewTestMCMSConfig(t, e.Env), rmnHome)
e.Env.Chains[newChain], newAddresses, rmnHome)
require.NoError(t, err)
require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses))
state, err = ccipdeployment.LoadOnchainState(e.Env)
Expand Down Expand Up @@ -117,10 +137,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 := commonchangeset.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)
commonchangeset.ExecuteProposal(t, e.Env, acceptOwnershipExec, state.Chains[sel].Timelock, sel)
}
for _, chain := range initialDeploy {
owner, err2 := state.Chains[chain].OnRamp.Owner(nil)
Expand Down
3 changes: 1 addition & 2 deletions deployment/ccip/changeset/deploy_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContracts

func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig) (deployment.ChangesetOutput, error) {
newAddresses := deployment.NewMemoryAddressBook()
err := ccipdeployment.DeployChainContractsForChains(env, newAddresses, c.HomeChainSelector, c.ChainSelectors, c.MCMSCfg)
err := ccipdeployment.DeployChainContractsForChains(env, newAddresses, c.HomeChainSelector, c.ChainSelectors)
if err != nil {
env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "newAddresses", newAddresses)
return deployment.ChangesetOutput{AddressBook: newAddresses}, deployment.MaybeDataErr(err)
Expand All @@ -28,7 +28,6 @@ func DeployChainContracts(env deployment.Environment, c DeployChainContractsConf
type DeployChainContractsConfig struct {
ChainSelectors []uint64
HomeChainSelector uint64
MCMSCfg ccipdeployment.MCMSConfig
}

func (c DeployChainContractsConfig) Validate() error {
Expand Down
18 changes: 17 additions & 1 deletion deployment/ccip/changeset/deploy_chain_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package changeset

import (
"math/big"
"testing"

"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"

"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"
)
Expand Down Expand Up @@ -45,11 +48,24 @@ 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,
HomeChainSelector: homeChainSel,
MCMSCfg: ccdeploy.NewTestMCMSConfig(t, e),
})
require.NoError(t, err)
require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook))
Expand Down
Loading

0 comments on commit c0b7a23

Please sign in to comment.