From 707356a15f7edd532ba5410efe0b8ea049b5eac1 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 19 Nov 2024 13:07:30 -0800 Subject: [PATCH 1/6] more updates --- deployment/ccip/add_lane_test.go | 4 +- .../ccip/changeset/active_candidate_test.go | 2 +- deployment/ccip/changeset/add_chain_test.go | 4 +- deployment/ccip/changeset/deploy_chain.go | 5 + deployment/ccip/changeset/home_chain.go | 1 + .../ccip/changeset/initial_add_chain.go | 37 ++++ deployment/ccip/changeset/initial_deploy.go | 21 +-- .../ccip/changeset/initial_deploy_test.go | 2 +- deployment/ccip/changeset/jobspec.go | 4 +- deployment/ccip/changeset/jobspec_test.go | 2 +- deployment/ccip/changeset/prerequisites.go | 12 +- deployment/ccip/changeset/save_existing.go | 2 + deployment/ccip/deploy.go | 159 ++++++++++-------- deployment/ccip/deploy_test.go | 4 +- deployment/ccip/state.go | 14 +- deployment/ccip/test_usdc_helpers.go | 11 +- .../ccip-tests/testsetups/test_helpers.go | 2 +- .../smoke/ccip_messaging_test.go | 2 +- integration-tests/smoke/ccip_test.go | 4 +- integration-tests/smoke/ccip_usdc_test.go | 36 ++-- integration-tests/smoke/fee_boosting_test.go | 3 +- 21 files changed, 205 insertions(+), 126 deletions(-) create mode 100644 deployment/ccip/changeset/initial_add_chain.go diff --git a/deployment/ccip/add_lane_test.go b/deployment/ccip/add_lane_test.go index 5edfdae1ab0..f407116a389 100644 --- a/deployment/ccip/add_lane_test.go +++ b/deployment/ccip/add_lane_test.go @@ -33,13 +33,13 @@ func TestAddLane(t *testing.T) { feeds := state.Chains[e.FeedChainSel].USDFeeds tokenConfig := NewTestTokenConfig(feeds) newAddresses := deployment.NewMemoryAddressBook() - err = DeployPrerequisiteChainContracts(e.Env, newAddresses, e.Env.AllChainSelectors()) + err = DeployPrerequisiteChainContracts(e.Env, newAddresses, e.Env.AllChainSelectors(), nil) 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{ + err = DeployCCIPContracts(e.Env, newAddresses, InitialAddChainConfig{ HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, TokenConfig: tokenConfig, diff --git a/deployment/ccip/changeset/active_candidate_test.go b/deployment/ccip/changeset/active_candidate_test.go index cd1d7604817..497caa950b9 100644 --- a/deployment/ccip/changeset/active_candidate_test.go +++ b/deployment/ccip/changeset/active_candidate_test.go @@ -38,7 +38,7 @@ func TestActiveCandidate(t *testing.T) { feeds := state.Chains[tenv.FeedChainSel].USDFeeds tokenConfig := ccdeploy.NewTestTokenConfig(feeds) - output, err := InitialDeploy(tenv.Env, ccdeploy.DeployCCIPContractConfig{ + output, err := InitialDeploy(tenv.Env, ccdeploy.InitialAddChainConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), diff --git a/deployment/ccip/changeset/add_chain_test.go b/deployment/ccip/changeset/add_chain_test.go index 2d79a76005d..2e96f63e87c 100644 --- a/deployment/ccip/changeset/add_chain_test.go +++ b/deployment/ccip/changeset/add_chain_test.go @@ -37,13 +37,13 @@ func TestAddChainInbound(t *testing.T) { // We deploy to the rest. initialDeploy := e.Env.AllChainSelectorsExcluding([]uint64{newChain}) newAddresses := deployment.NewMemoryAddressBook() - err = ccipdeployment.DeployPrerequisiteChainContracts(e.Env, newAddresses, initialDeploy) + err = ccipdeployment.DeployPrerequisiteChainContracts(e.Env, newAddresses, initialDeploy, nil) require.NoError(t, err) require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) newAddresses = deployment.NewMemoryAddressBook() - err = ccipdeployment.DeployCCIPContracts(e.Env, newAddresses, ccipdeployment.DeployCCIPContractConfig{ + err = ccipdeployment.DeployCCIPContracts(e.Env, newAddresses, ccipdeployment.InitialAddChainConfig{ HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, ChainsToDeploy: initialDeploy, diff --git a/deployment/ccip/changeset/deploy_chain.go b/deployment/ccip/changeset/deploy_chain.go index 68f350a9af7..4b84c725263 100644 --- a/deployment/ccip/changeset/deploy_chain.go +++ b/deployment/ccip/changeset/deploy_chain.go @@ -11,6 +11,11 @@ import ( var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContracts +// DeployChainContracts deploys all new CCIP v1.6 or later contracts for the given chains. +// It returns the new addresses for the contracts. +// If there is an error, it will return the successfully deployed addresses and the error so that the caller can call the +// changeset again with the same input to retry the failed deployment. +// Caller should update the environment's address book with the returned addresses. func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig) (deployment.ChangesetOutput, error) { newAddresses := deployment.NewMemoryAddressBook() err := ccipdeployment.DeployChainContractsForChains(env, newAddresses, c.HomeChainSelector, c.ChainSelectors, c.MCMSCfg) diff --git a/deployment/ccip/changeset/home_chain.go b/deployment/ccip/changeset/home_chain.go index 92b5b09c695..3480152bf53 100644 --- a/deployment/ccip/changeset/home_chain.go +++ b/deployment/ccip/changeset/home_chain.go @@ -16,6 +16,7 @@ import ( 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. +// Caller should update the environment's address book with the returned addresses. func DeployHomeChain(env deployment.Environment, cfg DeployHomeChainConfig) (deployment.ChangesetOutput, error) { err := cfg.Validate() if err != nil { diff --git a/deployment/ccip/changeset/initial_add_chain.go b/deployment/ccip/changeset/initial_add_chain.go new file mode 100644 index 00000000000..b0b20272a67 --- /dev/null +++ b/deployment/ccip/changeset/initial_add_chain.go @@ -0,0 +1,37 @@ +package changeset + +import ( + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" + + "github.com/smartcontractkit/chainlink/deployment" + ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" +) + +var _ deployment.ChangeSet[InitialAddChainConfig] = InitialAddChain + +func InitialAddChain(env deployment.Environment, c InitialAddChainConfig) (deployment.ChangesetOutput, error) { + newAddresses := deployment.NewMemoryAddressBook() + err := ccipdeployment.DeployChainContractsForChains(env, newAddresses, c.HomeChainSelector, c.ChainSelectors, c.MCMSCfg) + if err != nil { + env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "newAddresses", newAddresses) + return deployment.ChangesetOutput{AddressBook: newAddresses}, deployment.MaybeDataErr(err) + } + return deployment.ChangesetOutput{ + Proposals: []timelock.MCMSWithTimelockProposal{}, + AddressBook: newAddresses, + JobSpecs: nil, + }, nil +} + +type InitialAddChainConfig struct { + ChainSelectors []uint64 + HomeChainSelector uint64 + FeedChainSel uint64 + TokenConfig TokenConfig + // I believe it makes sense to have the same signers across all chains + // since that's the point MCMS. + MCMSConfig MCMSConfig + USDCConfig USDCConfig + // For setting OCR configuration + OCRSecrets deployment.OCRSecrets +} diff --git a/deployment/ccip/changeset/initial_deploy.go b/deployment/ccip/changeset/initial_deploy.go index de17834e8bd..fb47a51a890 100644 --- a/deployment/ccip/changeset/initial_deploy.go +++ b/deployment/ccip/changeset/initial_deploy.go @@ -1,30 +1,23 @@ package changeset import ( - "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" - "github.com/smartcontractkit/chainlink/deployment" ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" ) -var _ deployment.ChangeSet[ccipdeployment.DeployCCIPContractConfig] = InitialDeploy +var _ deployment.ChangeSet[ccipdeployment.InitialAddChainConfig] = InitialDeploy -func InitialDeploy(env deployment.Environment, c ccipdeployment.DeployCCIPContractConfig) (deployment.ChangesetOutput, error) { +func InitialDeploy(env deployment.Environment, c ccipdeployment.InitialAddChainConfig) (deployment.ChangesetOutput, error) { newAddresses := deployment.NewMemoryAddressBook() - err := ccipdeployment.DeployCCIPContracts(env, newAddresses, c) - if err != nil { - env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "newAddresses", newAddresses) - return deployment.ChangesetOutput{AddressBook: newAddresses}, deployment.MaybeDataErr(err) - } - js, err := ccipdeployment.NewCCIPJobSpecs(env.NodeIDs, env.Offchain) + err := ccipdeployment.InitialAddChain(env, newAddresses, c) if err != nil { - return deployment.ChangesetOutput{AddressBook: newAddresses}, err + env.Logger.Errorw("Failed to deploy initial chain", "err", err, "addressBook", newAddresses) + return deployment.ChangesetOutput{ + AddressBook: newAddresses, + }, err } return deployment.ChangesetOutput{ - Proposals: []timelock.MCMSWithTimelockProposal{}, AddressBook: newAddresses, - // Mapping of which nodes get which jobs. - JobSpecs: js, }, nil } diff --git a/deployment/ccip/changeset/initial_deploy_test.go b/deployment/ccip/changeset/initial_deploy_test.go index a3756022245..1816d276e26 100644 --- a/deployment/ccip/changeset/initial_deploy_test.go +++ b/deployment/ccip/changeset/initial_deploy_test.go @@ -32,7 +32,7 @@ func TestInitialDeploy(t *testing.T) { require.NoError(t, err) require.NoError(t, tenv.Env.ExistingAddresses.Merge(output.AddressBook)) - output, err = InitialDeploy(tenv.Env, ccdeploy.DeployCCIPContractConfig{ + output, err = InitialDeploy(tenv.Env, ccdeploy.InitialAddChainConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), diff --git a/deployment/ccip/changeset/jobspec.go b/deployment/ccip/changeset/jobspec.go index 76352ff364f..a5764e7e407 100644 --- a/deployment/ccip/changeset/jobspec.go +++ b/deployment/ccip/changeset/jobspec.go @@ -8,7 +8,9 @@ import ( ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" ) -func Jobspec(env deployment.Environment, _ any) (deployment.ChangesetOutput, error) { +// CCIPCapabilityJobspec returns the job specs for the CCIP capability. +// The caller needs to propose these job specs to the offchain system. +func CCIPCapabilityJobspec(env deployment.Environment, _ any) (deployment.ChangesetOutput, error) { js, err := ccipdeployment.NewCCIPJobSpecs(env.NodeIDs, env.Offchain) if err != nil { return deployment.ChangesetOutput{}, errors.Wrapf(err, "failed to create job specs") diff --git a/deployment/ccip/changeset/jobspec_test.go b/deployment/ccip/changeset/jobspec_test.go index 4a10bdc2436..21e80e85aa2 100644 --- a/deployment/ccip/changeset/jobspec_test.go +++ b/deployment/ccip/changeset/jobspec_test.go @@ -18,7 +18,7 @@ func TestJobSpecChangeset(t *testing.T) { Chains: 1, Nodes: 4, }) - output, err := Jobspec(e, nil) + output, err := CCIPCapabilityJobspec(e, nil) require.NoError(t, err) require.NotNil(t, output.JobSpecs) nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) diff --git a/deployment/ccip/changeset/prerequisites.go b/deployment/ccip/changeset/prerequisites.go index 20ff7f5a935..8bfe89d23cf 100644 --- a/deployment/ccip/changeset/prerequisites.go +++ b/deployment/ccip/changeset/prerequisites.go @@ -17,13 +17,15 @@ var ( // DeployPrerequisites deploys the pre-requisite contracts for CCIP // pre-requisite contracts are the contracts which can be reused from previous versions of CCIP +// Or the contracts which are already deployed on the chain ( for example, tokens, feeds, etc) +// Caller should update the environment's address book with the returned addresses. func DeployPrerequisites(env deployment.Environment, cfg DeployPrerequisiteConfig) (deployment.ChangesetOutput, error) { err := cfg.Validate() if err != nil { return deployment.ChangesetOutput{}, errors.Wrapf(deployment.ErrInvalidConfig, "%v", err) } ab := deployment.NewMemoryAddressBook() - err = ccipdeployment.DeployPrerequisiteChainContracts(env, ab, cfg.ChainSelectors) + err = ccipdeployment.DeployPrerequisiteChainContracts(env, ab, cfg.ChainSelectors, cfg.USDCEnabledChainSelectors) if err != nil { env.Logger.Errorw("Failed to deploy prerequisite contracts", "err", err, "addressBook", ab) return deployment.ChangesetOutput{ @@ -38,7 +40,8 @@ func DeployPrerequisites(env deployment.Environment, cfg DeployPrerequisiteConfi } type DeployPrerequisiteConfig struct { - ChainSelectors []uint64 + ChainSelectors []uint64 + USDCEnabledChainSelectors []uint64 // TODO handle tokens and feeds in prerequisite config Tokens map[ccipdeployment.TokenSymbol]common.Address Feeds map[ccipdeployment.TokenSymbol]common.Address @@ -50,5 +53,10 @@ func (c DeployPrerequisiteConfig) Validate() error { return fmt.Errorf("invalid chain selector: %d - %w", cs, err) } } + for _, cs := range c.USDCEnabledChainSelectors { + if err := deployment.IsValidChainSelector(cs); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", cs, err) + } + } return nil } diff --git a/deployment/ccip/changeset/save_existing.go b/deployment/ccip/changeset/save_existing.go index 76330a3a20a..a5177c8e49b 100644 --- a/deployment/ccip/changeset/save_existing.go +++ b/deployment/ccip/changeset/save_existing.go @@ -42,6 +42,8 @@ func (cfg ExistingContractsConfig) Validate() error { return nil } +// SaveExistingContracts saves the existing contracts to the address book. +// Caller should update the environment's address book with the returned addresses. func SaveExistingContracts(env deployment.Environment, cfg ExistingContractsConfig) (deployment.ChangesetOutput, error) { err := cfg.Validate() if err != nil { diff --git a/deployment/ccip/deploy.go b/deployment/ccip/deploy.go index d1f6866190d..97924697810 100644 --- a/deployment/ccip/deploy.go +++ b/deployment/ccip/deploy.go @@ -68,15 +68,20 @@ var ( USDCTokenPool deployment.ContractType = "USDCTokenPool" ) -func DeployPrerequisiteChainContracts(e deployment.Environment, ab deployment.AddressBook, selectors []uint64) error { +func DeployPrerequisiteChainContracts(e deployment.Environment, ab deployment.AddressBook, selectors []uint64, usdcEnabledChains []uint64) error { state, err := LoadOnchainState(e) if err != nil { e.Logger.Errorw("Failed to load existing onchain state", "err") return err } + mapUSDCEnabledChains := make(map[uint64]bool) + for _, chain := range usdcEnabledChains { + mapUSDCEnabledChains[chain] = true + } for _, sel := range selectors { chain := e.Chains[sel] - err = DeployPrerequisiteContracts(e, ab, state, chain) + usdcEnabled, ok := mapUSDCEnabledChains[sel] + err = DeployPrerequisiteContracts(e, ab, state, chain, ok && usdcEnabled) if err != nil { return errors.Wrapf(err, "failed to deploy prerequisite contracts for chain %d", sel) } @@ -86,7 +91,7 @@ func DeployPrerequisiteChainContracts(e deployment.Environment, ab deployment.Ad // DeployPrerequisiteContracts deploys the contracts that can be ported from previous CCIP version to the new one. // This is only required for staging and test environments where the contracts are not already deployed. -func DeployPrerequisiteContracts(e deployment.Environment, ab deployment.AddressBook, state CCIPOnChainState, chain deployment.Chain) error { +func DeployPrerequisiteContracts(e deployment.Environment, ab deployment.AddressBook, state CCIPOnChainState, chain deployment.Chain, usdcEnabled bool) error { lggr := e.Logger chainState, chainExists := state.Chains[chain.Selector] var weth9Contract *weth9.WETH9 @@ -266,43 +271,27 @@ func DeployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } else { e.Logger.Infow("router already deployed", "addr", chainState.Router.Address) } + if usdcEnabled { + token, pool, messenger, transmitter, err1 := DeployUSDC(e.Logger, chain, ab, rmnProxy.Address(), r.Address()) + if err1 != nil { + return err1 + } + e.Logger.Infow("Deployed USDC contracts", + "chainSelector", chain.Selector, + "token", token.Address(), + "pool", pool.Address(), + "transmitter", transmitter.Address(), + "messenger", messenger.Address(), + ) + } return nil } -type USDCConfig struct { - Enabled bool - USDCAttestationConfig -} - -type USDCAttestationConfig struct { - API string - APITimeout *commonconfig.Duration - APIInterval *commonconfig.Duration -} - -type DeployCCIPContractConfig struct { - HomeChainSel uint64 - 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 - USDCConfig USDCConfig - // For setting OCR configuration - OCRSecrets deployment.OCRSecrets -} - -// DeployCCIPContracts assumes the following contracts are deployed: -// - Capability registry -// - CCIP home -// - RMN home -// - Fee tokens on all chains. -// and present in ExistingAddressBook. -// 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 InitialAddChain( + e deployment.Environment, + ab deployment.AddressBook, + c InitialAddChainConfig, +) error { if c.OCRSecrets.IsEmpty() { return fmt.Errorf("OCR secrets are empty") } @@ -331,37 +320,6 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c e.Logger.Errorw("Failed to get rmn home", "err", err) return fmt.Errorf("rmn home not found") } - - usdcConfiguration := make(map[cciptypes.ChainSelector]pluginconfig.USDCCCTPTokenConfig) - for _, chainSel := range c.ChainsToDeploy { - chain, exists := e.Chains[chainSel] - if !exists { - return fmt.Errorf("chain %d not found", chainSel) - } - if c.USDCConfig.Enabled { - token, pool, messenger, transmitter, err1 := DeployUSDC(e.Logger, chain, ab, existingState.Chains[chainSel]) - if err1 != nil { - return err1 - } - e.Logger.Infow("Deployed USDC contracts", - "chainSelector", chainSel, - "token", token.Address(), - "pool", pool.Address(), - "transmitter", transmitter.Address(), - "messenger", messenger.Address(), - ) - - usdcConfiguration[cciptypes.ChainSelector(chainSel)] = pluginconfig.USDCCCTPTokenConfig{ - SourcePoolAddress: pool.Address().Hex(), - SourceMessageTransmitterAddr: transmitter.Address().Hex(), - } - } - } - err = DeployChainContractsForChains(e, ab, c.HomeChainSel, c.ChainsToDeploy, c.MCMSConfig) - if err != nil { - e.Logger.Errorw("Failed to deploy chain contracts", "err", err) - return err - } for _, chainSel := range c.ChainsToDeploy { chain, _ := e.Chains[chainSel] chainAddresses, err := ab.AddressesForChain(chain.Selector) @@ -376,8 +334,6 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c } tokenInfo := c.TokenConfig.GetTokenInfo(e.Logger, existingState.Chains[chainSel].LinkToken, existingState.Chains[chainSel].Weth9) - // TODO: Do we want to extract this? - // Add chain config for each chain. _, err = AddChainConfig( e.Logger, e.Chains[c.HomeChainSel], @@ -388,12 +344,12 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c return err } var tokenDataObserversConf []pluginconfig.TokenDataObserverConfig - if c.USDCConfig.Enabled { + if enabled, ok := c.USDCConfig.EnabledChainMap()[chainSel]; ok && enabled { tokenDataObserversConf = []pluginconfig.TokenDataObserverConfig{{ Type: pluginconfig.USDCCCTPHandlerType, Version: "1.0", USDCCCTPObserverConfig: &pluginconfig.USDCCCTPObserverConfig{ - Tokens: usdcConfiguration, + Tokens: c.USDCCCTPTokenConfig, AttestationAPI: c.USDCConfig.API, AttestationAPITimeout: c.USDCConfig.APITimeout, AttestationAPIInterval: c.USDCConfig.APIInterval, @@ -423,6 +379,63 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c return nil } +type USDCConfig struct { + EnabledChains []uint64 + USDCAttestationConfig +} + +func (cfg USDCConfig) EnabledChainMap() map[uint64]bool { + m := make(map[uint64]bool) + for _, chain := range cfg.EnabledChains { + m[chain] = true + } + return m +} + +type USDCAttestationConfig struct { + API string + APITimeout *commonconfig.Duration + APIInterval *commonconfig.Duration +} + +type InitialAddChainConfig struct { + HomeChainSel uint64 + 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 + USDCConfig USDCConfig + // For setting OCR configuration + OCRSecrets deployment.OCRSecrets + USDCCCTPTokenConfig map[cciptypes.ChainSelector]pluginconfig.USDCCCTPTokenConfig +} + +// DeployCCIPContracts assumes the following contracts are deployed: +// - Capability registry +// - CCIP home +// - RMN home +// - Fee tokens on all chains. +// and present in ExistingAddressBook. +// 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 InitialAddChainConfig) error { + err := DeployChainContractsForChains(e, ab, c.HomeChainSel, c.ChainsToDeploy, c.MCMSConfig) + if err != nil { + e.Logger.Errorw("Failed to deploy chain contracts", "err", err) + return err + } + err = InitialAddChain(e, ab, c) + if err != nil { + e.Logger.Errorw("Failed to add chain", "err", err) + return err + } + + return nil +} + type MCMSConfig struct { Admin config.Config Canceller config.Config @@ -549,7 +562,7 @@ func DeployChainContractsForChains(e deployment.Environment, ab deployment.Addre return err } if cr != CCIPCapabilityID { - return fmt.Errorf("capability registry does not support CCIP %s %s", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityID[:])) + return fmt.Errorf("unexpected mismatch between calculated ccip capability id (%s) and expected ccip capability id constant (%s)", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityID[:])) } capability, err := capReg.GetCapability(nil, CCIPCapabilityID) if err != nil { diff --git a/deployment/ccip/deploy_test.go b/deployment/ccip/deploy_test.go index 2ca9901ddbf..b46c001c49f 100644 --- a/deployment/ccip/deploy_test.go +++ b/deployment/ccip/deploy_test.go @@ -44,12 +44,12 @@ func TestDeployCCIPContracts(t *testing.T) { require.NotNil(t, s.Chains[feedChainSel].USDFeeds) newAddresses := deployment.NewMemoryAddressBook() - err = DeployPrerequisiteChainContracts(e, newAddresses, e.AllChainSelectors()) + err = DeployPrerequisiteChainContracts(e, newAddresses, e.AllChainSelectors(), nil) require.NoError(t, err) require.NoError(t, e.ExistingAddresses.Merge(newAddresses)) newAddresses = deployment.NewMemoryAddressBook() - err = DeployCCIPContracts(e, newAddresses, DeployCCIPContractConfig{ + err = DeployCCIPContracts(e, newAddresses, InitialAddChainConfig{ HomeChainSel: homeChainSel, FeedChainSel: feedChainSel, ChainsToDeploy: e.AllChainSelectors(), diff --git a/deployment/ccip/state.go b/deployment/ccip/state.go index dcbe52524cf..2eba2766557 100644 --- a/deployment/ccip/state.go +++ b/deployment/ccip/state.go @@ -2,6 +2,7 @@ package ccipdeployment import ( "fmt" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_usdc_token_messenger" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_usdc_token_transmitter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/usdc_token_pool" @@ -46,10 +47,15 @@ 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 { - OnRamp *onramp.OnRamp - OffRamp *offramp.OffRamp - FeeQuoter *fee_quoter.FeeQuoter - RMNProxyNew *rmn_proxy_contract.RMNProxyContract + OnRamp *onramp.OnRamp + OffRamp *offramp.OffRamp + FeeQuoter *fee_quoter.FeeQuoter + // This is the new RMNProxy contract that will be used for testing RMNRemote before migration + // Initially RMNProxyNew will point to RMNRemote + RMNProxyNew *rmn_proxy_contract.RMNProxyContract + // Existing RMNProxy contract that is used in production, This already has 1.5 RMN set. + // once RMNRemote is tested with RMNProxyNew, as part of migration + // RMNProxyExisting will point to RMNRemote. This will switch over CCIP 1.5 to 1.6 RMNProxyExisting *rmn_proxy_contract.RMNProxyContract NonceManager *nonce_manager.NonceManager TokenAdminRegistry *token_admin_registry.TokenAdminRegistry diff --git a/deployment/ccip/test_usdc_helpers.go b/deployment/ccip/test_usdc_helpers.go index 787ca328a8e..c80dd679e39 100644 --- a/deployment/ccip/test_usdc_helpers.go +++ b/deployment/ccip/test_usdc_helpers.go @@ -1,16 +1,18 @@ package ccipdeployment import ( + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink-ccip/pkg/reader" "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/mock_usdc_token_messenger" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_usdc_token_transmitter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/usdc_token_pool" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" - "math/big" ) func ConfigureUSDCTokenPools( @@ -117,7 +119,8 @@ func DeployUSDC( lggr logger.Logger, chain deployment.Chain, addresses deployment.AddressBook, - state CCIPChainState, + rmnProxy common.Address, + router common.Address, ) ( *burn_mint_erc677.BurnMintERC677, *usdc_token_pool.USDCTokenPool, @@ -212,8 +215,8 @@ func DeployUSDC( messenger.Address, token.Address, []common.Address{}, - state.RMNProxyExisting.Address(), - state.Router.Address(), + rmnProxy, + router, ) return deployment.ContractDeploy[*usdc_token_pool.USDCTokenPool]{ Address: tokenPoolAddress, diff --git a/integration-tests/ccip-tests/testsetups/test_helpers.go b/integration-tests/ccip-tests/testsetups/test_helpers.go index 4a480cdaa40..5e508e2104f 100644 --- a/integration-tests/ccip-tests/testsetups/test_helpers.go +++ b/integration-tests/ccip-tests/testsetups/test_helpers.go @@ -151,7 +151,7 @@ func NewLocalDevEnvironmentWithRMN( // Deploy CCIP contracts. newAddresses := deployment.NewMemoryAddressBook() - err = ccipdeployment.DeployCCIPContracts(tenv.Env, newAddresses, ccipdeployment.DeployCCIPContractConfig{ + err = ccipdeployment.DeployCCIPContracts(tenv.Env, newAddresses, ccipdeployment.InitialAddChainConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), diff --git a/integration-tests/smoke/ccip_messaging_test.go b/integration-tests/smoke/ccip_messaging_test.go index 4aa9ba34229..b802d03af3a 100644 --- a/integration-tests/smoke/ccip_messaging_test.go +++ b/integration-tests/smoke/ccip_messaging_test.go @@ -74,7 +74,7 @@ func Test_CCIPMessaging(t *testing.T) { tokenConfig := ccdeploy.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) // Apply migration - output, err = changeset.InitialDeploy(e.Env, ccdeploy.DeployCCIPContractConfig{ + output, err = changeset.InitialDeploy(e.Env, ccdeploy.InitialAddChainConfig{ HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, ChainsToDeploy: allChainSelectors, diff --git a/integration-tests/smoke/ccip_test.go b/integration-tests/smoke/ccip_test.go index 007a3c37e52..280bc0e9b4b 100644 --- a/integration-tests/smoke/ccip_test.go +++ b/integration-tests/smoke/ccip_test.go @@ -36,7 +36,7 @@ func TestInitialDeployOnLocal(t *testing.T) { require.NoError(t, tenv.Env.ExistingAddresses.Merge(output.AddressBook)) // Apply migration - output, err = changeset.InitialDeploy(tenv.Env, ccdeploy.DeployCCIPContractConfig{ + output, err = changeset.InitialDeploy(tenv.Env, ccdeploy.InitialAddChainConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), @@ -127,7 +127,7 @@ func TestTokenTransfer(t *testing.T) { require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) // Apply migration - output, err = changeset.InitialDeploy(e, ccdeploy.DeployCCIPContractConfig{ + output, err = changeset.InitialDeploy(e, ccdeploy.InitialAddChainConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: e.AllChainSelectors(), diff --git a/integration-tests/smoke/ccip_usdc_test.go b/integration-tests/smoke/ccip_usdc_test.go index 091912b26fb..fcf25f3121b 100644 --- a/integration-tests/smoke/ccip_usdc_test.go +++ b/integration-tests/smoke/ccip_usdc_test.go @@ -9,16 +9,17 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink-ccip/pluginconfig" "github.com/stretchr/testify/require" "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" @@ -57,37 +58,44 @@ func TestUSDCTokenTransfer(t *testing.T) { 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(), + ChainSelectors: e.AllChainSelectors(), + USDCEnabledChainSelectors: e.AllChainSelectors(), }) require.NoError(t, err) require.NoError(t, tenv.Env.ExistingAddresses.Merge(output.AddressBook)) + state, err = ccdeploy.LoadOnchainState(e) + require.NoError(t, err) + USDCCCTPConfig := make(map[ccipocr3.ChainSelector]pluginconfig.USDCCCTPTokenConfig) + for _, chain := range e.AllChainSelectors() { + require.NotNil(t, state.Chains[chain].MockUSDCTokenMessenger) + require.NotNil(t, state.Chains[chain].MockUSDCTransmitter) + require.NotNil(t, state.Chains[chain].USDCTokenPool) + USDCCCTPConfig[ccipocr3.ChainSelector(chain)] = pluginconfig.USDCCCTPTokenConfig{ + SourcePoolAddress: state.Chains[chain].USDCTokenPool.Address().String(), + SourceMessageTransmitterAddr: state.Chains[chain].MockUSDCTransmitter.Address().String(), + } + } + // Apply migration - output, err = changeset.InitialDeploy(e, ccdeploy.DeployCCIPContractConfig{ + output, err = changeset.InitialDeploy(e, ccdeploy.InitialAddChainConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: e.AllChainSelectors(), - TokenConfig: tokenConfig, + TokenConfig: ccdeploy.NewTestTokenConfig(feeds), MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e), OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), USDCConfig: ccdeploy.USDCConfig{ - Enabled: true, + EnabledChains: e.AllChainSelectors(), USDCAttestationConfig: ccdeploy.USDCAttestationConfig{ API: endpoint, APITimeout: commonconfig.MustNewDuration(time.Second), APIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), }, }, + USDCCCTPTokenConfig: USDCCCTPConfig, }) require.NoError(t, err) require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) diff --git a/integration-tests/smoke/fee_boosting_test.go b/integration-tests/smoke/fee_boosting_test.go index 625200360e8..a3fb0cb2fae 100644 --- a/integration-tests/smoke/fee_boosting_test.go +++ b/integration-tests/smoke/fee_boosting_test.go @@ -10,6 +10,7 @@ import ( "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" @@ -57,7 +58,7 @@ func Test_CCIPFeeBoosting(t *testing.T) { tokenConfig := ccdeploy.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) // Apply migration - output, err = changeset.InitialDeploy(e.Env, ccdeploy.DeployCCIPContractConfig{ + output, err = changeset.InitialDeploy(e.Env, ccdeploy.InitialAddChainConfig{ HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, ChainsToDeploy: allChainSelectors, From 22529a229f2d496656b595aee64e13c0b5e75969 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 19 Nov 2024 13:20:35 -0800 Subject: [PATCH 2/6] fix --- .../ccip/changeset/initial_add_chain.go | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/deployment/ccip/changeset/initial_add_chain.go b/deployment/ccip/changeset/initial_add_chain.go index b0b20272a67..3c7bfc087a7 100644 --- a/deployment/ccip/changeset/initial_add_chain.go +++ b/deployment/ccip/changeset/initial_add_chain.go @@ -7,11 +7,11 @@ import ( ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" ) -var _ deployment.ChangeSet[InitialAddChainConfig] = InitialAddChain +var _ deployment.ChangeSet[ccipdeployment.InitialAddChainConfig] = InitialAddChain -func InitialAddChain(env deployment.Environment, c InitialAddChainConfig) (deployment.ChangesetOutput, error) { +func InitialAddChain(env deployment.Environment, c ccipdeployment.InitialAddChainConfig) (deployment.ChangesetOutput, error) { newAddresses := deployment.NewMemoryAddressBook() - err := ccipdeployment.DeployChainContractsForChains(env, newAddresses, c.HomeChainSelector, c.ChainSelectors, c.MCMSCfg) + err := ccipdeployment.InitialAddChain(env, newAddresses, c) if err != nil { env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "newAddresses", newAddresses) return deployment.ChangesetOutput{AddressBook: newAddresses}, deployment.MaybeDataErr(err) @@ -22,16 +22,3 @@ func InitialAddChain(env deployment.Environment, c InitialAddChainConfig) (deplo JobSpecs: nil, }, nil } - -type InitialAddChainConfig struct { - ChainSelectors []uint64 - HomeChainSelector uint64 - FeedChainSel uint64 - TokenConfig TokenConfig - // I believe it makes sense to have the same signers across all chains - // since that's the point MCMS. - MCMSConfig MCMSConfig - USDCConfig USDCConfig - // For setting OCR configuration - OCRSecrets deployment.OCRSecrets -} From d8f5d2403de1d82dae8b4a0e6378d3465f1dc8e9 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 19 Nov 2024 13:39:50 -0800 Subject: [PATCH 3/6] updates --- deployment/ccip/changeset/add_chain_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/ccip/changeset/add_chain_test.go b/deployment/ccip/changeset/add_chain_test.go index 2e96f63e87c..8c8b2e58bd1 100644 --- a/deployment/ccip/changeset/add_chain_test.go +++ b/deployment/ccip/changeset/add_chain_test.go @@ -73,7 +73,7 @@ func TestAddChainInbound(t *testing.T) { // Deploy contracts to new chain newAddresses = deployment.NewMemoryAddressBook() - err = ccipdeployment.DeployPrerequisiteChainContracts(e.Env, newAddresses, []uint64{newChain}) + err = ccipdeployment.DeployPrerequisiteChainContracts(e.Env, newAddresses, []uint64{newChain}, nil) require.NoError(t, err) require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) newAddresses = deployment.NewMemoryAddressBook() From 6eb7b0c9488137ff29a037f7c17d1db458bb7484 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 19 Nov 2024 13:41:05 -0800 Subject: [PATCH 4/6] revert --- deployment/ccip/changeset/initial_deploy.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/deployment/ccip/changeset/initial_deploy.go b/deployment/ccip/changeset/initial_deploy.go index fb47a51a890..c99554c3946 100644 --- a/deployment/ccip/changeset/initial_deploy.go +++ b/deployment/ccip/changeset/initial_deploy.go @@ -1,6 +1,8 @@ package changeset import ( + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" + "github.com/smartcontractkit/chainlink/deployment" ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" @@ -10,14 +12,18 @@ var _ deployment.ChangeSet[ccipdeployment.InitialAddChainConfig] = InitialDeploy func InitialDeploy(env deployment.Environment, c ccipdeployment.InitialAddChainConfig) (deployment.ChangesetOutput, error) { newAddresses := deployment.NewMemoryAddressBook() - err := ccipdeployment.InitialAddChain(env, newAddresses, c) + err := ccipdeployment.DeployCCIPContracts(env, newAddresses, c) + if err != nil { + env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "newAddresses", newAddresses) + return deployment.ChangesetOutput{AddressBook: newAddresses}, deployment.MaybeDataErr(err) + } + js, err := ccipdeployment.NewCCIPJobSpecs(env.NodeIDs, env.Offchain) if err != nil { - env.Logger.Errorw("Failed to deploy initial chain", "err", err, "addressBook", newAddresses) - return deployment.ChangesetOutput{ - AddressBook: newAddresses, - }, err + return deployment.ChangesetOutput{AddressBook: newAddresses}, err } return deployment.ChangesetOutput{ + Proposals: []timelock.MCMSWithTimelockProposal{}, AddressBook: newAddresses, + JobSpecs: js, }, nil } From 687cc6f6236344dfbaf1366420cb3027a1afdb1b Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 19 Nov 2024 13:57:42 -0800 Subject: [PATCH 5/6] go imports --- integration-tests/smoke/ccip_usdc_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration-tests/smoke/ccip_usdc_test.go b/integration-tests/smoke/ccip_usdc_test.go index fcf25f3121b..d3d819b3603 100644 --- a/integration-tests/smoke/ccip_usdc_test.go +++ b/integration-tests/smoke/ccip_usdc_test.go @@ -9,9 +9,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" "github.com/smartcontractkit/chainlink-ccip/pluginconfig" - "github.com/stretchr/testify/require" "golang.org/x/exp/maps" From bde95190b0f374895f67a218b6b2de498597d26c Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 19 Nov 2024 18:45:42 -0800 Subject: [PATCH 6/6] more updates --- .../ccip/changeset/active_candidate_test.go | 22 +++-- deployment/ccip/changeset/add_chain_test.go | 16 ++-- ...t.go => composite_changeset_apply_test.go} | 64 +++++++------- .../ccip/changeset/initial_add_chain.go | 12 ++- deployment/ccip/changeset/initial_deploy.go | 29 ------- deployment/ccip/deploy.go | 66 +++++++++++++- deployment/ccip/propose.go | 65 +++++++++----- deployment/ccip/test_helpers.go | 85 +++++++++++++++++-- deployment/changeset.go | 16 ++++ deployment/environment.go | 11 +++ .../smoke/ccip_messaging_test.go | 2 +- integration-tests/smoke/ccip_test.go | 4 +- integration-tests/smoke/ccip_usdc_test.go | 4 +- integration-tests/smoke/fee_boosting_test.go | 2 +- 14 files changed, 283 insertions(+), 115 deletions(-) rename deployment/ccip/changeset/{initial_deploy_test.go => composite_changeset_apply_test.go} (70%) delete mode 100644 deployment/ccip/changeset/initial_deploy.go diff --git a/deployment/ccip/changeset/active_candidate_test.go b/deployment/ccip/changeset/active_candidate_test.go index 497caa950b9..795d17c5206 100644 --- a/deployment/ccip/changeset/active_candidate_test.go +++ b/deployment/ccip/changeset/active_candidate_test.go @@ -38,7 +38,7 @@ func TestActiveCandidate(t *testing.T) { feeds := state.Chains[tenv.FeedChainSel].USDFeeds tokenConfig := ccdeploy.NewTestTokenConfig(feeds) - output, err := InitialDeploy(tenv.Env, ccdeploy.InitialAddChainConfig{ + output, err := DeployCCIPOnNewChains(tenv.Env, ccdeploy.InitialAddChainConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), @@ -114,9 +114,10 @@ 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, err := ccdeploy.SignProposalWithTestSigner(e, acceptOwnershipProposal) + require.NoError(t, err) for _, sel := range e.AllChainSelectors() { - ccdeploy.ExecuteProposal(t, e, acceptOwnershipExec, state, sel) + require.NoError(t, ccdeploy.ExecuteProposal(e, acceptOwnershipExec, state, sel)) } // Apply the accept ownership proposal to all the chains. @@ -177,8 +178,9 @@ 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, err := ccdeploy.SignProposalWithTestSigner(e, setCommitCandidateProposal) + require.NoError(t, err) + require.NoError(t, ccdeploy.ExecuteProposal(e, setCommitCandidateSigned, state, homeCS)) // create the op for the commit plugin as well setExecCandidateOp, err := ccdeploy.SetCandidateOnExistingDon( @@ -195,8 +197,9 @@ 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, err := ccdeploy.SignProposalWithTestSigner(e, setExecCandidateProposal) + require.NoError(t, err) + require.NoError(t, ccdeploy.ExecuteProposal(e, setExecCandidateSigned, state, homeCS)) // check setup was successful by confirming number of nodes from cap reg donInfo, err = state.Chains[homeCS].CapabilityRegistry.GetDON(nil, donID) @@ -222,8 +225,9 @@ 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, err := ccdeploy.SignProposalWithTestSigner(e, promoteProposal) + require.NoError(t, err) + require.NoError(t, ccdeploy.ExecuteProposal(e, promoteSigned, state, 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 8c8b2e58bd1..41ce730b392 100644 --- a/deployment/ccip/changeset/add_chain_test.go +++ b/deployment/ccip/changeset/add_chain_test.go @@ -29,7 +29,8 @@ import ( func TestAddChainInbound(t *testing.T) { // 4 chains where the 4th is added after initial deployment. - e := ccipdeployment.NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 4, 4) + lggr := logger.TestLogger(t) + e := ccipdeployment.NewMemoryEnvironmentWithJobs(t, lggr, 4, 4) state, err := ccipdeployment.LoadOnchainState(e.Env) require.NoError(t, err) // Take first non-home chain as the new chain. @@ -117,10 +118,11 @@ 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, err := ccipdeployment.SignProposalWithTestSigner(e.Env, acceptOwnershipProposal) + require.NoError(t, err) // Apply the accept ownership proposal to all the chains. for _, sel := range initialDeploy { - ccipdeployment.ExecuteProposal(t, e.Env, acceptOwnershipExec, state, sel) + require.NoError(t, ccipdeployment.ExecuteProposal(e.Env, acceptOwnershipExec, state, sel)) } for _, chain := range initialDeploy { owner, err2 := state.Chains[chain].OnRamp.Owner(nil) @@ -140,7 +142,7 @@ func TestAddChainInbound(t *testing.T) { // Generate and sign inbound proposal to new 4th chain. chainInboundChangeset, err := NewChainInboundChangeset(e.Env, state, e.HomeChainSel, newChain, initialDeploy) require.NoError(t, err) - ccipdeployment.ProcessChangeset(t, e.Env, chainInboundChangeset) + ccipdeployment.ProcessChangesetProposals(e.Env, chainInboundChangeset) // TODO This currently is not working - Able to send the request here but request gets stuck in execution // Send a new message and expect that this is delivered once the chain is completely set up as inbound @@ -149,17 +151,17 @@ func TestAddChainInbound(t *testing.T) { t.Logf("Executing add don and set candidate proposal for commit plugin on chain %d", newChain) addDonChangeset, err := AddDonAndSetCandidateChangeset(state, e.Env, nodes, deployment.XXXGenerateTestOCRSecrets(), e.HomeChainSel, e.FeedChainSel, newChain, tokenConfig, types.PluginTypeCCIPCommit) require.NoError(t, err) - ccipdeployment.ProcessChangeset(t, e.Env, addDonChangeset) + ccipdeployment.ProcessChangesetProposals(e.Env, addDonChangeset) t.Logf("Executing promote candidate proposal for exec plugin on chain %d", newChain) setCandidateForExecChangeset, err := SetCandidatePluginChangeset(state, e.Env, nodes, deployment.XXXGenerateTestOCRSecrets(), e.HomeChainSel, e.FeedChainSel, newChain, tokenConfig, types.PluginTypeCCIPExec) require.NoError(t, err) - ccipdeployment.ProcessChangeset(t, e.Env, setCandidateForExecChangeset) + ccipdeployment.ProcessChangesetProposals(e.Env, setCandidateForExecChangeset) t.Logf("Executing promote candidate proposal for both commit and exec plugins on chain %d", newChain) donPromoteChangeset, err := PromoteAllCandidatesChangeset(state, e.HomeChainSel, newChain, nodes) require.NoError(t, err) - ccipdeployment.ProcessChangeset(t, e.Env, donPromoteChangeset) + ccipdeployment.ProcessChangesetProposals(e.Env, donPromoteChangeset) // verify if the configs are updated require.NoError(t, ccipdeployment.ValidateCCIPHomeConfigSetUp( diff --git a/deployment/ccip/changeset/initial_deploy_test.go b/deployment/ccip/changeset/composite_changeset_apply_test.go similarity index 70% rename from deployment/ccip/changeset/initial_deploy_test.go rename to deployment/ccip/changeset/composite_changeset_apply_test.go index 1816d276e26..7747ca39d83 100644 --- a/deployment/ccip/changeset/initial_deploy_test.go +++ b/deployment/ccip/changeset/composite_changeset_apply_test.go @@ -5,8 +5,6 @@ import ( "github.com/ethereum/go-ethereum/common" - jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" - "github.com/smartcontractkit/chainlink/deployment" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" @@ -26,42 +24,44 @@ func TestInitialDeploy(t *testing.T) { state, err := ccdeploy.LoadOnchainState(tenv.Env) require.NoError(t, err) - output, err := DeployPrerequisites(e, DeployPrerequisiteConfig{ - ChainSelectors: tenv.Env.AllChainSelectors(), - }) - require.NoError(t, err) - require.NoError(t, tenv.Env.ExistingAddresses.Merge(output.AddressBook)) - - output, err = InitialDeploy(tenv.Env, ccdeploy.InitialAddChainConfig{ - HomeChainSel: tenv.HomeChainSel, - FeedChainSel: tenv.FeedChainSel, - ChainsToDeploy: tenv.Env.AllChainSelectors(), - TokenConfig: ccdeploy.NewTestTokenConfig(state.Chains[tenv.FeedChainSel].USDFeeds), - MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e), - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), - }) + mcmsCfg := ccdeploy.NewTestMCMSConfig(t, e) + changesets := []deployment.ChangesetApplication{ + { + Changeset: deployment.WrapChangeSet(DeployPrerequisites), + Config: DeployPrerequisiteConfig{ + ChainSelectors: tenv.Env.AllChainSelectors(), + }, + }, + { + Changeset: deployment.WrapChangeSet(DeployChainContracts), + Config: DeployChainContractsConfig{ + ChainSelectors: tenv.Env.AllChainSelectors(), + HomeChainSelector: tenv.HomeChainSel, + MCMSCfg: mcmsCfg, + }, + }, + { + Changeset: deployment.WrapChangeSet(InitialAddChain), + Config: ccdeploy.InitialAddChainConfig{ + HomeChainSel: tenv.HomeChainSel, + FeedChainSel: tenv.FeedChainSel, + ChainsToDeploy: tenv.Env.AllChainSelectors(), + TokenConfig: ccdeploy.NewTestTokenConfig(state.Chains[tenv.FeedChainSel].USDFeeds), + MCMSConfig: mcmsCfg, + OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + }, + }, + { + Changeset: deployment.WrapChangeSet(CCIPCapabilityJobspec), + }, + } + tenv.Env, err = ccdeploy.ApplyChangesets(ctx, tenv.Env, changesets) require.NoError(t, err) - // Get new state after migration. - require.NoError(t, tenv.Env.ExistingAddresses.Merge(output.AddressBook)) state, err = ccdeploy.LoadOnchainState(e) require.NoError(t, err) require.NotNil(t, state.Chains[tenv.HomeChainSel].LinkToken) // 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/deployment/ccip/changeset/initial_add_chain.go b/deployment/ccip/changeset/initial_add_chain.go index 3c7bfc087a7..5b769028646 100644 --- a/deployment/ccip/changeset/initial_add_chain.go +++ b/deployment/ccip/changeset/initial_add_chain.go @@ -1,15 +1,25 @@ package changeset import ( + "fmt" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" "github.com/smartcontractkit/chainlink/deployment" ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" ) -var _ deployment.ChangeSet[ccipdeployment.InitialAddChainConfig] = InitialAddChain +var InitialAddChainCS deployment.ChangeSet[ccipdeployment.InitialAddChainConfig] = InitialAddChain +// InitialAddChain enables new chains as destination for CCIP +// It performs the following steps: +// - AddChainConfig + AddDON (candidate->primary promotion i.e. init) on the home chain +// - SetOCR3Config on the remote chain +// InitialAddChain assumes that the home chain is already enabled and all CCIP contracts are already deployed. func InitialAddChain(env deployment.Environment, c ccipdeployment.InitialAddChainConfig) (deployment.ChangesetOutput, error) { + if err := c.Validate(); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("invalid InitialAddChainConfig: %w", err) + } newAddresses := deployment.NewMemoryAddressBook() err := ccipdeployment.InitialAddChain(env, newAddresses, c) if err != nil { diff --git a/deployment/ccip/changeset/initial_deploy.go b/deployment/ccip/changeset/initial_deploy.go deleted file mode 100644 index c99554c3946..00000000000 --- a/deployment/ccip/changeset/initial_deploy.go +++ /dev/null @@ -1,29 +0,0 @@ -package changeset - -import ( - "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" - - "github.com/smartcontractkit/chainlink/deployment" - - ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip" -) - -var _ deployment.ChangeSet[ccipdeployment.InitialAddChainConfig] = InitialDeploy - -func InitialDeploy(env deployment.Environment, c ccipdeployment.InitialAddChainConfig) (deployment.ChangesetOutput, error) { - newAddresses := deployment.NewMemoryAddressBook() - err := ccipdeployment.DeployCCIPContracts(env, newAddresses, c) - if err != nil { - env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "newAddresses", newAddresses) - return deployment.ChangesetOutput{AddressBook: newAddresses}, deployment.MaybeDataErr(err) - } - js, err := ccipdeployment.NewCCIPJobSpecs(env.NodeIDs, env.Offchain) - if err != nil { - return deployment.ChangesetOutput{AddressBook: newAddresses}, err - } - return deployment.ChangesetOutput{ - Proposals: []timelock.MCMSWithTimelockProposal{}, - AddressBook: newAddresses, - JobSpecs: js, - }, nil -} diff --git a/deployment/ccip/deploy.go b/deployment/ccip/deploy.go index 97924697810..cbca6f21510 100644 --- a/deployment/ccip/deploy.go +++ b/deployment/ccip/deploy.go @@ -349,7 +349,7 @@ func InitialAddChain( Type: pluginconfig.USDCCCTPHandlerType, Version: "1.0", USDCCCTPObserverConfig: &pluginconfig.USDCCCTPObserverConfig{ - Tokens: c.USDCCCTPTokenConfig, + Tokens: c.USDCConfig.CCTPTokenConfig, AttestationAPI: c.USDCConfig.API, AttestationAPITimeout: c.USDCConfig.APITimeout, AttestationAPIInterval: c.USDCConfig.APIInterval, @@ -382,6 +382,7 @@ func InitialAddChain( type USDCConfig struct { EnabledChains []uint64 USDCAttestationConfig + CCTPTokenConfig map[cciptypes.ChainSelector]pluginconfig.USDCCCTPTokenConfig } func (cfg USDCConfig) EnabledChainMap() map[uint64]bool { @@ -408,8 +409,67 @@ type InitialAddChainConfig struct { MCMSConfig MCMSConfig USDCConfig USDCConfig // For setting OCR configuration - OCRSecrets deployment.OCRSecrets - USDCCCTPTokenConfig map[cciptypes.ChainSelector]pluginconfig.USDCCCTPTokenConfig + OCRSecrets deployment.OCRSecrets +} + +func (c InitialAddChainConfig) Validate() error { + if err := deployment.IsValidChainSelector(c.HomeChainSel); err != nil { + return fmt.Errorf("invalid home chain selector: %d - %w", c.HomeChainSel, err) + } + if err := deployment.IsValidChainSelector(c.FeedChainSel); err != nil { + return fmt.Errorf("invalid feed chain selector: %d - %w", c.FeedChainSel, err) + } + mapChainsToDeploy := make(map[uint64]bool) + for _, cs := range c.ChainsToDeploy { + mapChainsToDeploy[cs] = true + if err := deployment.IsValidChainSelector(cs); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", cs, err) + } + } + for token := range c.TokenConfig.TokenSymbolToInfo { + if err := c.TokenConfig.TokenSymbolToInfo[token].Validate(); err != nil { + return fmt.Errorf("invalid token config for token %s: %w", token, err) + } + } + if err := c.MCMSConfig.Admin.Validate(); err != nil { + return fmt.Errorf("invalid admin MCMS config: %w", err) + } + if err := c.MCMSConfig.Canceller.Validate(); err != nil { + return fmt.Errorf("invalid canceller MCMS config: %w", err) + } + if err := c.MCMSConfig.Bypasser.Validate(); err != nil { + return fmt.Errorf("invalid bypasser MCMS config: %w", err) + } + if err := c.MCMSConfig.Proposer.Validate(); err != nil { + return fmt.Errorf("invalid proposer MCMS config: %w", err) + } + if len(c.MCMSConfig.Executors) == 0 { + return fmt.Errorf("no MCMS executors provided") + } + if c.OCRSecrets.IsEmpty() { + return fmt.Errorf("no OCR secrets provided") + } + usdcEnabledChainMap := c.USDCConfig.EnabledChainMap() + for chain := range usdcEnabledChainMap { + if _, exists := mapChainsToDeploy[chain]; !exists { + return fmt.Errorf("chain %d is not in chains to deploy", chain) + } + if err := deployment.IsValidChainSelector(chain); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", chain, err) + } + } + for chain := range c.USDCConfig.CCTPTokenConfig { + if _, exists := mapChainsToDeploy[uint64(chain)]; !exists { + return fmt.Errorf("chain %d is not in chains to deploy", chain) + } + if _, exists := usdcEnabledChainMap[uint64(chain)]; !exists { + return fmt.Errorf("chain %d is not enabled in USDC config", chain) + } + if err := deployment.IsValidChainSelector(uint64(chain)); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", chain, err) + } + } + return nil } // DeployCCIPContracts assumes the following contracts are deployed: diff --git a/deployment/ccip/propose.go b/deployment/ccip/propose.go index 9d6ac417968..9e31ce19d59 100644 --- a/deployment/ccip/propose.go +++ b/deployment/ccip/propose.go @@ -61,38 +61,52 @@ func NewTestMCMSConfig(t *testing.T, e deployment.Environment) MCMSConfig { } } -func SignProposal(t *testing.T, env deployment.Environment, proposal *timelock.MCMSWithTimelockProposal) *mcms.Executor { +func SignProposalWithTestSigner(env deployment.Environment, proposal *timelock.MCMSWithTimelockProposal) (*mcms.Executor, error) { executorClients := make(map[mcms.ChainIdentifier]mcms.ContractDeployBackend) for _, chain := range env.Chains { chainselc, exists := chainsel.ChainBySelector(chain.Selector) - require.True(t, exists) + if !exists { + return nil, fmt.Errorf("chain not found for selector %d", chain.Selector) + } chainSel := mcms.ChainIdentifier(chainselc.Selector) executorClients[chainSel] = chain.Client } executor, err := proposal.ToExecutor(true) - require.NoError(t, err) + if err != nil { + return nil, fmt.Errorf("failed to create executor: %w", err) + } payload, err := executor.SigningHash() - require.NoError(t, err) + if err != nil { + return nil, fmt.Errorf("failed to create signing hash: %w", err) + } // Sign the payload sig, err := crypto.Sign(payload.Bytes(), TestXXXMCMSSigner) - require.NoError(t, err) + if err != nil { + return nil, fmt.Errorf("failed to sign payload: %w", err) + } mcmSig, err := mcms.NewSignatureFromBytes(sig) - require.NoError(t, err) + if err != nil { + return nil, fmt.Errorf("failed to create signature: %w", err) + } executor.Proposal.AddSignature(mcmSig) - require.NoError(t, executor.Proposal.Validate()) - return executor + err = executor.Proposal.Validate() + if err != nil { + return nil, fmt.Errorf("failed to validate proposal: %w", err) + } + return executor, nil } -func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Executor, - state CCIPOnChainState, sel uint64) { - t.Log("Executing proposal on chain", sel) +func ExecuteProposal(env deployment.Environment, executor *mcms.Executor, state CCIPOnChainState, sel uint64) error { + env.Logger.Infow("Executing proposal on chain", "selector", 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)) + return fmt.Errorf("failed to set root: %w", deployment.MaybeDataErr(err2)) } _, err2 = env.Chains[sel].Confirm(tx) - require.NoError(t, err2) + if err2 != nil { + return fmt.Errorf("failed to confirm tx: %w", 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. @@ -100,23 +114,29 @@ func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Ex 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) + if err3 != nil { + return fmt.Errorf("failed to execute on chain: %w", deployment.MaybeDataErr(err3)) + } block, err3 := env.Chains[sel].Confirm(opTx) - require.NoError(t, err3) - t.Log("executed", chainOp) + if err3 != nil { + return fmt.Errorf("failed to confirm tx: %w", err3) + } + env.Logger.Infow("executed", "chainOp", chainOp) it, err3 := state.Chains[sel].Timelock.FilterCallScheduled(&bind.FilterOpts{ Start: block, End: &block, Context: context.Background(), }, nil, nil) - require.NoError(t, err3) + if err3 != nil { + return fmt.Errorf("failed to filter call scheduled: %w", 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) + env.Logger.Infow("scheduled", "event", it.Event) calls = append(calls, owner_helpers.RBACTimelockCall{ Target: it.Event.Target, Data: it.Event.Data, @@ -125,12 +145,17 @@ func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Ex } tx, err := state.Chains[sel].Timelock.ExecuteBatch( env.Chains[sel].DeployerKey, calls, pred, salt) - require.NoError(t, err) + if err != nil { + return fmt.Errorf("failed to execute batch: %w", deployment.MaybeDataErr(err)) + } _, err = env.Chains[sel].Confirm(tx) - require.NoError(t, err) + if err != nil { + return fmt.Errorf("failed to confirm tx: %w", err) + } } } } + return nil } func GenerateAcceptOwnershipProposal( diff --git a/deployment/ccip/test_helpers.go b/deployment/ccip/test_helpers.go index d682a72eddc..7121dff0459 100644 --- a/deployment/ccip/test_helpers.go +++ b/deployment/ccip/test_helpers.go @@ -502,23 +502,29 @@ func ConfirmRequestOnSourceAndDest(t *testing.T, env deployment.Environment, sta return nil } -func ProcessChangeset(t *testing.T, e deployment.Environment, c deployment.ChangesetOutput) { - - // TODO: Add support for jobspecs as well - +// TODO : Remove this one to replace with ApplyChangeset +func ProcessChangesetProposals(e deployment.Environment, c deployment.ChangesetOutput) error { // sign and execute all proposals provided if len(c.Proposals) != 0 { state, err := LoadOnchainState(e) - require.NoError(t, err) + if err != nil { + return fmt.Errorf("failed to load onchain state: %w", err) + } for _, prop := range c.Proposals { chains := mapset.NewSet[uint64]() for _, op := range prop.Transactions { chains.Add(uint64(op.ChainIdentifier)) } - signed := SignProposal(t, e, &prop) + signed, err := SignProposalWithTestSigner(e, &prop) + if err != nil { + return fmt.Errorf("failed to sign proposal: %w", err) + } for _, sel := range chains.ToSlice() { - ExecuteProposal(t, e, signed, state, sel) + err := ExecuteProposal(e, signed, state, sel) + if err != nil { + return fmt.Errorf("failed to execute proposal: %w", err) + } } } } @@ -526,8 +532,11 @@ func ProcessChangeset(t *testing.T, e deployment.Environment, c deployment.Chang // merge address books if c.AddressBook != nil { err := e.ExistingAddresses.Merge(c.AddressBook) - require.NoError(t, err) + if err != nil { + return fmt.Errorf("failed to merge address books: %w", err) + } } + return nil } func DeployTransferableToken( @@ -795,3 +804,63 @@ func deployTransferTokenOneEnd( return tokenContract.Contract, tokenPool.Contract, nil } + +// ApplyChangesets TODO : Move this to deployment package once MCMS is extracted to common +// ApplyChangesets applies the changeset applications to the environment and returns the updated environment. +func ApplyChangesets(ctx context.Context, e deployment.Environment, changesetApplications []deployment.ChangesetApplication) (deployment.Environment, error) { + currentEnv, err := e.Copy() + if err != nil { + return e, fmt.Errorf("failed to copy environment: %w", err) + } + for i, csa := range changesetApplications { + out, err := csa.Changeset(currentEnv, csa.Config) + if err != nil { + return e, fmt.Errorf("failed to apply changeset at index %d: %w", i, err) + } + if out.AddressBook != nil { + err := currentEnv.ExistingAddresses.Merge(out.AddressBook) + if err != nil { + return e, fmt.Errorf("failed to merge address book: %w", err) + } + } + if out.JobSpecs != nil { + for nodeID, jobs := range out.JobSpecs { + for _, job := range jobs { + // Note these auto-accept + _, err := e.Offchain.ProposeJob(ctx, + &jobv1.ProposeJobRequest{ + NodeId: nodeID, + Spec: job, + }) + if err != nil { + return e, fmt.Errorf("failed to propose job: %w", err) + } + } + } + } + if out.Proposals != nil { + state, err := LoadOnchainState(e) + if err != nil { + return deployment.Environment{}, fmt.Errorf("failed to load onchain state: %w", err) + } + for _, prop := range out.Proposals { + chains := mapset.NewSet[uint64]() + for _, op := range prop.Transactions { + chains.Add(uint64(op.ChainIdentifier)) + } + + signed, err := SignProposalWithTestSigner(e, &prop) + if err != nil { + return deployment.Environment{}, fmt.Errorf("failed to sign proposal: %w", err) + } + for _, sel := range chains.ToSlice() { + err := ExecuteProposal(e, signed, state, sel) + if err != nil { + return deployment.Environment{}, fmt.Errorf("failed to execute proposal: %w", err) + } + } + } + } + } + return currentEnv, nil +} diff --git a/deployment/changeset.go b/deployment/changeset.go index abce4942203..c67929644b0 100644 --- a/deployment/changeset.go +++ b/deployment/changeset.go @@ -3,6 +3,7 @@ package deployment import ( "encoding/json" "errors" + "fmt" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" ) @@ -33,3 +34,18 @@ type ChangesetOutput struct { // ViewState produces a product specific JSON representation of // the on and offchain state of the environment. type ViewState func(e Environment) (json.Marshaler, error) + +type ChangesetApplication struct { + Changeset func(e Environment, config any) (ChangesetOutput, error) + Config any +} + +func WrapChangeSet[C any](fn ChangeSet[C]) func(e Environment, config any) (ChangesetOutput, error) { + return func(e Environment, config any) (ChangesetOutput, error) { + c, ok := config.(C) + if !ok { + return ChangesetOutput{}, fmt.Errorf("invalid config type, expected %T", c) + } + return fn(e, c) + } +} diff --git a/deployment/environment.go b/deployment/environment.go index 5d4e782b0fe..203c9032941 100644 --- a/deployment/environment.go +++ b/deployment/environment.go @@ -79,6 +79,17 @@ type Environment struct { MockAdapter *test_env.Killgrave } +func (e Environment) Copy() (Environment, error) { + newEnv := e + addr := NewMemoryAddressBook() + err := addr.Merge(e.ExistingAddresses) + if err != nil { + return Environment{}, err + } + newEnv.ExistingAddresses = addr + return newEnv, nil +} + func NewEnvironment( name string, logger logger.Logger, diff --git a/integration-tests/smoke/ccip_messaging_test.go b/integration-tests/smoke/ccip_messaging_test.go index b802d03af3a..c0cb3c848a9 100644 --- a/integration-tests/smoke/ccip_messaging_test.go +++ b/integration-tests/smoke/ccip_messaging_test.go @@ -74,7 +74,7 @@ func Test_CCIPMessaging(t *testing.T) { tokenConfig := ccdeploy.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) // Apply migration - output, err = changeset.InitialDeploy(e.Env, ccdeploy.InitialAddChainConfig{ + output, err = changeset.DeployCCIPOnNewChains(e.Env, ccdeploy.InitialAddChainConfig{ HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, ChainsToDeploy: allChainSelectors, diff --git a/integration-tests/smoke/ccip_test.go b/integration-tests/smoke/ccip_test.go index 280bc0e9b4b..c3f898a5e80 100644 --- a/integration-tests/smoke/ccip_test.go +++ b/integration-tests/smoke/ccip_test.go @@ -36,7 +36,7 @@ func TestInitialDeployOnLocal(t *testing.T) { require.NoError(t, tenv.Env.ExistingAddresses.Merge(output.AddressBook)) // Apply migration - output, err = changeset.InitialDeploy(tenv.Env, ccdeploy.InitialAddChainConfig{ + output, err = changeset.DeployCCIPOnNewChains(tenv.Env, ccdeploy.InitialAddChainConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), @@ -127,7 +127,7 @@ func TestTokenTransfer(t *testing.T) { require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) // Apply migration - output, err = changeset.InitialDeploy(e, ccdeploy.InitialAddChainConfig{ + output, err = changeset.DeployCCIPOnNewChains(e, ccdeploy.InitialAddChainConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: e.AllChainSelectors(), diff --git a/integration-tests/smoke/ccip_usdc_test.go b/integration-tests/smoke/ccip_usdc_test.go index d3d819b3603..91e80b2e05f 100644 --- a/integration-tests/smoke/ccip_usdc_test.go +++ b/integration-tests/smoke/ccip_usdc_test.go @@ -81,7 +81,7 @@ func TestUSDCTokenTransfer(t *testing.T) { } // Apply migration - output, err = changeset.InitialDeploy(e, ccdeploy.InitialAddChainConfig{ + output, err = changeset.DeployCCIPOnNewChains(e, ccdeploy.InitialAddChainConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: e.AllChainSelectors(), @@ -95,8 +95,8 @@ func TestUSDCTokenTransfer(t *testing.T) { APITimeout: commonconfig.MustNewDuration(time.Second), APIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), }, + CCTPTokenConfig: USDCCCTPConfig, }, - USDCCCTPTokenConfig: USDCCCTPConfig, }) require.NoError(t, err) require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) diff --git a/integration-tests/smoke/fee_boosting_test.go b/integration-tests/smoke/fee_boosting_test.go index a3fb0cb2fae..24fb5e64e3e 100644 --- a/integration-tests/smoke/fee_boosting_test.go +++ b/integration-tests/smoke/fee_boosting_test.go @@ -58,7 +58,7 @@ func Test_CCIPFeeBoosting(t *testing.T) { tokenConfig := ccdeploy.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) // Apply migration - output, err = changeset.InitialDeploy(e.Env, ccdeploy.InitialAddChainConfig{ + output, err = changeset.DeployCCIPOnNewChains(e.Env, ccdeploy.InitialAddChainConfig{ HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, ChainsToDeploy: allChainSelectors,