From b41bebe19bbd59393db761ce04633c96d2001af7 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Mon, 9 Dec 2024 13:38:59 -0800 Subject: [PATCH 01/29] just start --- .../ccip/changeset/v1_5/cs_deploy_chains.go | 45 ++++++++++++++ deployment/ccip/changeset/v1_5/state.go | 62 +++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 deployment/ccip/changeset/v1_5/cs_deploy_chains.go create mode 100644 deployment/ccip/changeset/v1_5/state.go diff --git a/deployment/ccip/changeset/v1_5/cs_deploy_chains.go b/deployment/ccip/changeset/v1_5/cs_deploy_chains.go new file mode 100644 index 00000000000..9d4cc107cf0 --- /dev/null +++ b/deployment/ccip/changeset/v1_5/cs_deploy_chains.go @@ -0,0 +1,45 @@ +package v1_5 + +import ( + "fmt" + + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" + + "github.com/smartcontractkit/chainlink/deployment" +) + +var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContracts + +type DeployChainContractsConfig struct { + ChainSelectors []uint64 +} + +func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig) (deployment.ChangesetOutput, error) { + if err := c.Validate(); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig: %w", err) + } + newAddresses := deployment.NewMemoryAddressBook() + err := deployChainContractsForChains(env, newAddresses, 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) + } + return deployment.ChangesetOutput{ + Proposals: []timelock.MCMSWithTimelockProposal{}, + AddressBook: newAddresses, + JobSpecs: nil, + }, nil +} + +func (c DeployChainContractsConfig) Validate() error { + for _, cs := range c.ChainSelectors { + if err := deployment.IsValidChainSelector(cs); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", cs, err) + } + } + return nil +} + +func deployChainContractsForChains(env deployment.Environment, ab deployment.AddressBook, selectors []uint64) error { + +} diff --git a/deployment/ccip/changeset/v1_5/state.go b/deployment/ccip/changeset/v1_5/state.go new file mode 100644 index 00000000000..b15248e0b3a --- /dev/null +++ b/deployment/ccip/changeset/v1_5/state.go @@ -0,0 +1,62 @@ +package v1_5 + +import ( + commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" + "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/registry_module_owner_custom" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_proxy_contract" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/token_admin_registry" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/usdc_token_pool" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/weth9" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" +) + +// CCIPOnChainState state always derivable from an address book. +// Offchain state always derivable from a list of nodeIds. +// Note can translate this into Go struct needed for MCMS/Docs/UI. +type CCIPOnChainState struct { + // Populated go bindings for the appropriate version for all contracts. + // We would hold 2 versions of each contract here. Once we upgrade we can phase out the old one. + // When generating bindings, make sure the package name corresponds to the version. + Chains map[uint64]CCIPChainState +} + +// 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 + commoncs.LinkTokenState + commoncs.StaticLinkTokenState + OnRamp map[uint64]*evm_2_evm_onramp.EVM2EVMOnRamp + CommitStore map[uint64]*commit_store.CommitStore + OffRamp map[uint64]*evm_2_evm_offramp.EVM2EVMOffRamp + RMNProxy *rmn_proxy_contract.RMNProxyContract + TokenAdminRegistry *token_admin_registry.TokenAdminRegistry + RegistryModule *registry_module_owner_custom.RegistryModuleOwnerCustom + Router *router.Router + Weth9 *weth9.WETH9 + MockRMN *mock_rmn_contract.MockRMNContract + // Map between token Descriptor (e.g. LinkSymbol, WethSymbol) + // and the respective token contract + // This is more of an illustration of how we'll have tokens, and it might need some work later to work properly. + // Not all tokens will be burn and mint tokens. + BurnMintTokens677 map[TokenSymbol]*burn_mint_erc677.BurnMintERC677 + BurnMintTokenPools map[TokenSymbol]*burn_mint_token_pool.BurnMintTokenPool + // Map between token Symbol (e.g. LinkSymbol, WethSymbol) + // and the respective aggregator USD feed contract + USDFeeds map[TokenSymbol]*aggregator_v3_interface.AggregatorV3Interface + + // Test contracts + Receiver *maybe_revert_message_receiver.MaybeRevertMessageReceiver + TestRouter *router.Router + USDCTokenPool *usdc_token_pool.USDCTokenPool + MockUSDCTransmitter *mock_usdc_token_transmitter.MockE2EUSDCTransmitter + MockUSDCTokenMessenger *mock_usdc_token_messenger.MockE2EUSDCTokenMessenger +} From 91bbfe5732f1c55828d7dabc44420b952d6cadc7 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Mon, 9 Dec 2024 19:06:33 -0800 Subject: [PATCH 02/29] placeholders --- deployment/ccip/changeset/v1_5/state.go | 259 +++++++++++++++++++++++- 1 file changed, 256 insertions(+), 3 deletions(-) diff --git a/deployment/ccip/changeset/v1_5/state.go b/deployment/ccip/changeset/v1_5/state.go index b15248e0b3a..f6431f6978e 100644 --- a/deployment/ccip/changeset/v1_5/state.go +++ b/deployment/ccip/changeset/v1_5/state.go @@ -1,21 +1,40 @@ package v1_5 import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/burn_mint_token_pool" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_home" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" "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/nonce_manager" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_proxy_contract" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_remote" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/token_admin_registry" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/usdc_token_pool" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/weth9" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/multicall3" ) // CCIPOnChainState state always derivable from an address book. @@ -40,6 +59,7 @@ type CCIPChainState struct { RMNProxy *rmn_proxy_contract.RMNProxyContract TokenAdminRegistry *token_admin_registry.TokenAdminRegistry RegistryModule *registry_module_owner_custom.RegistryModuleOwnerCustom + FeeQuoter *fee_quoter.FeeQuoter Router *router.Router Weth9 *weth9.WETH9 MockRMN *mock_rmn_contract.MockRMNContract @@ -47,11 +67,11 @@ type CCIPChainState struct { // and the respective token contract // This is more of an illustration of how we'll have tokens, and it might need some work later to work properly. // Not all tokens will be burn and mint tokens. - BurnMintTokens677 map[TokenSymbol]*burn_mint_erc677.BurnMintERC677 - BurnMintTokenPools map[TokenSymbol]*burn_mint_token_pool.BurnMintTokenPool + BurnMintTokens677 map[changeset.TokenSymbol]*burn_mint_erc677.BurnMintERC677 + BurnMintTokenPools map[changeset.TokenSymbol]*burn_mint_token_pool.BurnMintTokenPool // Map between token Symbol (e.g. LinkSymbol, WethSymbol) // and the respective aggregator USD feed contract - USDFeeds map[TokenSymbol]*aggregator_v3_interface.AggregatorV3Interface + USDFeeds map[changeset.TokenSymbol]*aggregator_v3_interface.AggregatorV3Interface // Test contracts Receiver *maybe_revert_message_receiver.MaybeRevertMessageReceiver @@ -60,3 +80,236 @@ type CCIPChainState struct { MockUSDCTransmitter *mock_usdc_token_transmitter.MockE2EUSDCTransmitter MockUSDCTokenMessenger *mock_usdc_token_messenger.MockE2EUSDCTokenMessenger } + +func LoadChainState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (CCIPChainState, error) { + var state CCIPChainState + mcmsWithTimelock, err := commoncs.MaybeLoadMCMSWithTimelockState(chain, addresses) + if err != nil { + return state, err + } + state.MCMSWithTimelockState = *mcmsWithTimelock + + linkState, err := commoncs.MaybeLoadLinkTokenState(chain, addresses) + if err != nil { + return state, err + } + state.LinkTokenState = *linkState + staticLinkState, err := commoncs.MaybeLoadStaticLinkTokenState(chain, addresses) + if err != nil { + return state, err + } + state.StaticLinkTokenState = *staticLinkState + 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(), + deployment.NewTypeAndVersion(commontypes.LinkToken, deployment.Version1_0_0).String(), + deployment.NewTypeAndVersion(commontypes.StaticLinkToken, deployment.Version1_0_0).String(): + // Skip common contracts, they are already loaded. + continue + case deployment.NewTypeAndVersion(changeset.OnRamp, deployment.Version1_5_0).String(): + onRampC, err := onramp.NewOnRamp(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.OnRamp = onRampC + case deployment.NewTypeAndVersion(OffRamp, deployment.Version1_6_0_dev).String(): + offRamp, err := offramp.NewOffRamp(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.OffRamp = offRamp + case deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_0_0).String(): + armProxy, err := rmn_proxy_contract.NewRMNProxyContract(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.RMNProxyExisting = armProxy + case deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_6_0_dev).String(): + armProxy, err := rmn_proxy_contract.NewRMNProxyContract(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.RMNProxyNew = armProxy + case deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_6_0_dev).String(): + armProxy, err := rmn_proxy_contract.NewRMNProxyContract(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.RMNProxyNew = armProxy + case deployment.NewTypeAndVersion(MockRMN, deployment.Version1_0_0).String(): + mockRMN, err := mock_rmn_contract.NewMockRMNContract(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.MockRMN = mockRMN + case deployment.NewTypeAndVersion(RMNRemote, deployment.Version1_6_0_dev).String(): + rmnRemote, err := rmn_remote.NewRMNRemote(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.RMNRemote = rmnRemote + case deployment.NewTypeAndVersion(RMNHome, deployment.Version1_6_0_dev).String(): + rmnHome, err := rmn_home.NewRMNHome(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.RMNHome = rmnHome + case deployment.NewTypeAndVersion(WETH9, deployment.Version1_0_0).String(): + weth9, err := weth9.NewWETH9(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.Weth9 = weth9 + case deployment.NewTypeAndVersion(NonceManager, deployment.Version1_6_0_dev).String(): + nm, err := nonce_manager.NewNonceManager(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.NonceManager = nm + case deployment.NewTypeAndVersion(CommitStore, deployment.Version1_5_0).String(): + cs, err := commit_store.NewCommitStore(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.CommitStore = cs + case deployment.NewTypeAndVersion(TokenAdminRegistry, deployment.Version1_5_0).String(): + tm, err := token_admin_registry.NewTokenAdminRegistry(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.TokenAdminRegistry = tm + case deployment.NewTypeAndVersion(RegistryModule, deployment.Version1_5_0).String(): + rm, err := registry_module_owner_custom.NewRegistryModuleOwnerCustom(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.RegistryModule = rm + case deployment.NewTypeAndVersion(Router, deployment.Version1_2_0).String(): + r, err := router.NewRouter(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.Router = r + case deployment.NewTypeAndVersion(TestRouter, deployment.Version1_2_0).String(): + r, err := router.NewRouter(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.TestRouter = r + case deployment.NewTypeAndVersion(FeeQuoter, deployment.Version1_6_0_dev).String(): + fq, err := fee_quoter.NewFeeQuoter(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.FeeQuoter = fq + case deployment.NewTypeAndVersion(USDCToken, deployment.Version1_0_0).String(): + ut, err := burn_mint_erc677.NewBurnMintERC677(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.BurnMintTokens677 = map[TokenSymbol]*burn_mint_erc677.BurnMintERC677{ + USDCSymbol: ut, + } + case deployment.NewTypeAndVersion(USDCTokenPool, deployment.Version1_0_0).String(): + utp, err := usdc_token_pool.NewUSDCTokenPool(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.USDCTokenPool = utp + case deployment.NewTypeAndVersion(USDCMockTransmitter, deployment.Version1_0_0).String(): + umt, err := mock_usdc_token_transmitter.NewMockE2EUSDCTransmitter(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.MockUSDCTransmitter = umt + case deployment.NewTypeAndVersion(USDCTokenMessenger, deployment.Version1_0_0).String(): + utm, err := mock_usdc_token_messenger.NewMockE2EUSDCTokenMessenger(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.MockUSDCTokenMessenger = utm + case deployment.NewTypeAndVersion(CCIPHome, deployment.Version1_6_0_dev).String(): + ccipHome, err := ccip_home.NewCCIPHome(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.CCIPHome = ccipHome + case deployment.NewTypeAndVersion(CCIPConfig, deployment.Version1_0_0).String(): + // TODO: Remove once staging upgraded. + ccipConfig, err := ccip_config.NewCCIPConfig(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.CCIPConfig = ccipConfig + case deployment.NewTypeAndVersion(CCIPReceiver, deployment.Version1_0_0).String(): + mr, err := maybe_revert_message_receiver.NewMaybeRevertMessageReceiver(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.Receiver = mr + case deployment.NewTypeAndVersion(Multicall3, deployment.Version1_0_0).String(): + mc, err := multicall3.NewMulticall3(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.Multicall3 = mc + case deployment.NewTypeAndVersion(PriceFeed, deployment.Version1_0_0).String(): + feed, err := aggregator_v3_interface.NewAggregatorV3Interface(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + if state.USDFeeds == nil { + state.USDFeeds = make(map[TokenSymbol]*aggregator_v3_interface.AggregatorV3Interface) + } + desc, err := feed.Description(&bind.CallOpts{}) + if err != nil { + return state, err + } + key, ok := MockDescriptionToTokenSymbol[desc] + if !ok { + return state, fmt.Errorf("unknown feed description %s", desc) + } + state.USDFeeds[key] = feed + case deployment.NewTypeAndVersion(BurnMintTokenPool, deployment.Version1_5_1).String(): + pool, err := burn_mint_token_pool.NewBurnMintTokenPool(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + if state.BurnMintTokenPools == nil { + state.BurnMintTokenPools = make(map[TokenSymbol]*burn_mint_token_pool.BurnMintTokenPool) + } + tokAddress, err := pool.GetToken(nil) + if err != nil { + return state, err + } + tok, err := erc20.NewERC20(tokAddress, chain.Client) + if err != nil { + return state, err + } + symbol, err := tok.Symbol(nil) + if err != nil { + return state, err + } + state.BurnMintTokenPools[TokenSymbol(symbol)] = pool + case deployment.NewTypeAndVersion(BurnMintToken, deployment.Version1_0_0).String(): + tok, err := burn_mint_erc677.NewBurnMintERC677(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + if state.BurnMintTokens677 == nil { + state.BurnMintTokens677 = make(map[TokenSymbol]*burn_mint_erc677.BurnMintERC677) + } + symbol, err := tok.Symbol(nil) + if err != nil { + return state, fmt.Errorf("failed to get token symbol of token at %s: %w", address, err) + } + state.BurnMintTokens677[TokenSymbol(symbol)] = tok + default: + return state, fmt.Errorf("unknown contract %s", tvStr) + } + } + return state, nil +} From 9a29a0061104e0db8e0f2914689353489c4434c6 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 10 Dec 2024 07:49:27 -0800 Subject: [PATCH 03/29] state --- deployment/ccip/changeset/v1_5/state.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/deployment/ccip/changeset/v1_5/state.go b/deployment/ccip/changeset/v1_5/state.go index f6431f6978e..29ad0d1bcea 100644 --- a/deployment/ccip/changeset/v1_5/state.go +++ b/deployment/ccip/changeset/v1_5/state.go @@ -22,8 +22,6 @@ import ( "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/nonce_manager" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_proxy_contract" @@ -110,13 +108,20 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type // Skip common contracts, they are already loaded. continue case deployment.NewTypeAndVersion(changeset.OnRamp, deployment.Version1_5_0).String(): - onRampC, err := onramp.NewOnRamp(common.HexToAddress(address), chain.Client) + onRampC, err := evm_2_evm_onramp.NewEVM2EVMOnRamp(common.HexToAddress(address), chain.Client) if err != nil { return state, err } - state.OnRamp = onRampC - case deployment.NewTypeAndVersion(OffRamp, deployment.Version1_6_0_dev).String(): - offRamp, err := offramp.NewOffRamp(common.HexToAddress(address), chain.Client) + sCfg, err := onRampC.GetStaticConfig(nil) + if err != nil { + return state, err + } + if state.OnRamp == nil { + state.OnRamp = make(map[uint64]*evm_2_evm_onramp.EVM2EVMOnRamp) + } + state.OnRamp[sCfg.DestChainSelector] = onRampC + case deployment.NewTypeAndVersion(changeset.OffRamp, deployment.Version1_5_0).String(): + offRamp, err := evm(common.HexToAddress(address), chain.Client) if err != nil { return state, err } From a4bebbaf710cf493ecab1d71849f34918135e8d7 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 10 Dec 2024 13:32:18 -0800 Subject: [PATCH 04/29] change --- deployment/ccip/changeset/v1_5/state.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment/ccip/changeset/v1_5/state.go b/deployment/ccip/changeset/v1_5/state.go index 29ad0d1bcea..338301fb708 100644 --- a/deployment/ccip/changeset/v1_5/state.go +++ b/deployment/ccip/changeset/v1_5/state.go @@ -35,10 +35,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/multicall3" ) -// CCIPOnChainState state always derivable from an address book. +// CCIPOnChainState_Legacy state always derivable from an address book. // Offchain state always derivable from a list of nodeIds. // Note can translate this into Go struct needed for MCMS/Docs/UI. -type CCIPOnChainState struct { +type CCIPOnChainState_Legacy struct { // Populated go bindings for the appropriate version for all contracts. // We would hold 2 versions of each contract here. Once we upgrade we can phase out the old one. // When generating bindings, make sure the package name corresponds to the version. From 86d394793f790997b91c59f67b376878bcaad5a8 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 10 Dec 2024 17:03:29 -0800 Subject: [PATCH 05/29] updates --- deployment/ccip/changeset/cs_deploy_chain.go | 44 +-- deployment/ccip/changeset/cs_prerequisites.go | 6 +- deployment/ccip/changeset/state.go | 145 +++++--- .../ccip/changeset/v1_5/cs_deploy_chains.go | 103 +++++- deployment/ccip/changeset/v1_5/state.go | 320 +----------------- 5 files changed, 215 insertions(+), 403 deletions(-) diff --git a/deployment/ccip/changeset/cs_deploy_chain.go b/deployment/ccip/changeset/cs_deploy_chain.go index e2762b27578..e9e9d03f8c9 100644 --- a/deployment/ccip/changeset/cs_deploy_chain.go +++ b/deployment/ccip/changeset/cs_deploy_chain.go @@ -19,7 +19,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_proxy_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_remote" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" ) @@ -205,6 +204,22 @@ func deployChainContracts( if chainState.Router == nil { return fmt.Errorf("router not found for chain %s, deploy the prerequisites first", chain.String()) } + rmnProxyContract := chainState.RMNProxy + if chainState.RMNProxy == nil { + e.Logger.Errorw("RMNProxy not found", "chain", chain.String()) + return fmt.Errorf("rmn proxy not found for chain %s, deploy the prerequisites first", chain.String()) + } + var rmnLegacyAddr common.Address + if chainState.MockRMN != nil { + rmnLegacyAddr = chainState.MockRMN.Address() + } + // If RMN is deployed, set rmnLegacyAddr to the RMN address + if chainState.RMN != nil { + rmnLegacyAddr = chainState.RMN.Address() + } + if rmnLegacyAddr == (common.Address{}) { + e.Logger.Warnf("No legacy RMN contract found for chain %s, will not setRMN in RMNRemote", chain.String()) + } if chainState.Receiver == nil { _, err := deployment.DeployContract(e.Logger, chain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver] { @@ -233,8 +248,7 @@ func deployChainContracts( chain.DeployerKey, chain.Client, chain.Selector, - // Indicates no legacy RMN contract - common.HexToAddress("0x0"), + rmnLegacyAddr, ) return deployment.ContractDeploy[*rmn_remote.RMNRemote]{ rmnRemoteAddr, rmnRemote, tx, deployment.NewTypeAndVersion(RMNRemote, deployment.Version1_6_0_dev), err2, @@ -267,30 +281,6 @@ func deployChainContracts( return err } - // we deploy a new RMNProxy so that RMNRemote can be tested first before pointing it to the main Existing RMNProxy - // To differentiate between the two RMNProxies, we will deploy new one with Version1_6_0_dev - rmnProxyContract := chainState.RMNProxyNew - if chainState.RMNProxyNew == nil { - // we deploy a new rmnproxy contract to test RMNRemote - rmnProxy, err := deployment.DeployContract(e.Logger, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract] { - rmnProxyAddr, tx, rmnProxy, err2 := rmn_proxy_contract.DeployRMNProxyContract( - chain.DeployerKey, - chain.Client, - rmnRemoteContract.Address(), - ) - return deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract]{ - rmnProxyAddr, rmnProxy, tx, deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_6_0_dev), err2, - } - }) - if err != nil { - e.Logger.Errorw("Failed to deploy RMNProxyNew", "chain", chain.String(), "err", err) - return err - } - rmnProxyContract = rmnProxy.Contract - } else { - e.Logger.Infow("rmn proxy already deployed", "chain", chain.String(), "addr", chainState.RMNProxyNew.Address) - } if chainState.TestRouter == nil { _, err := deployment.DeployContract(e.Logger, chain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*router.Router] { diff --git a/deployment/ccip/changeset/cs_prerequisites.go b/deployment/ccip/changeset/cs_prerequisites.go index 2386d3bb784..e98697ed8d6 100644 --- a/deployment/ccip/changeset/cs_prerequisites.go +++ b/deployment/ccip/changeset/cs_prerequisites.go @@ -133,7 +133,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address weth9Contract = chainState.Weth9 tokenAdminReg = chainState.TokenAdminRegistry registryModule = chainState.RegistryModule - rmnProxy = chainState.RMNProxyExisting + rmnProxy = chainState.RMNProxy r = chainState.Router mc3 = chainState.Multicall3 } @@ -141,7 +141,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address // we want to replicate the mainnet scenario where RMNProxy is already deployed with some existing RMN // This will need us to use two different RMNProxy contracts // 1. RMNProxyNew with RMNRemote - ( deployed later in chain contracts) - // 2. RMNProxyExisting with mockRMN - ( deployed here, replicating the behavior of existing RMNProxy with already set RMN) + // 2. RMNProxy with mockRMN - ( deployed here, replicating the behavior of existing RMNProxy with already set RMN) rmn, err := deployment.DeployContract(lggr, chain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract] { rmnAddr, tx2, rmn, err2 := mock_rmn_contract.DeployMockRMNContract( @@ -168,7 +168,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } }) if err != nil { - lggr.Errorw("Failed to deploy RMNProxyExisting", "chain", chain.String(), "err", err) + lggr.Errorw("Failed to deploy RMNProxy", "chain", chain.String(), "err", err) return err } rmnProxy = rmnProxyContract.Contract diff --git a/deployment/ccip/changeset/state.go b/deployment/ccip/changeset/state.go index 22ae59fc360..248ad57f409 100644 --- a/deployment/ccip/changeset/state.go +++ b/deployment/ccip/changeset/state.go @@ -4,6 +4,11 @@ import ( "fmt" burn_mint_token_pool "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/burn_mint_token_pool_1_4_0" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/erc20" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_usdc_token_messenger" @@ -15,6 +20,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/deployment" + legacychangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_5" "github.com/smartcontractkit/chainlink/deployment/ccip/view" "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_0" "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_2" @@ -24,7 +30,6 @@ import ( 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" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" @@ -53,7 +58,6 @@ var ( ARMProxy deployment.ContractType = "ARMProxy" WETH9 deployment.ContractType = "WETH9" Router deployment.ContractType = "Router" - CommitStore deployment.ContractType = "CommitStore" TokenAdminRegistry deployment.ContractType = "TokenAdminRegistry" RegistryModule deployment.ContractType = "RegistryModuleOwnerCustom" NonceManager deployment.ContractType = "NonceManager" @@ -75,6 +79,11 @@ var ( USDCMockTransmitter deployment.ContractType = "USDCMockTransmitter" USDCTokenMessenger deployment.ContractType = "USDCTokenMessenger" USDCTokenPool deployment.ContractType = "USDCTokenPool" + + // Legacy contracts + CommitStore deployment.ContractType = "CommitStore" + PriceRegistry deployment.ContractType = "PriceRegistry" + RMN deployment.ContractType = "RMN" ) // CCIPChainState holds a Go binding for all the currently deployed CCIP contracts @@ -83,26 +92,16 @@ type CCIPChainState struct { commoncs.MCMSWithTimelockState commoncs.LinkTokenState commoncs.StaticLinkTokenState - OnRamp *onramp.OnRamp - OffRamp *offramp.OffRamp - FeeQuoter *fee_quoter.FeeQuoter - // We need 2 RMNProxy contracts because we are in the process of migrating to a new version. - // We will switch to the existing one once the migration is complete. - // 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 existing 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 + OnRamp *onramp.OnRamp + OffRamp *offramp.OffRamp + FeeQuoter *fee_quoter.FeeQuoter + RMNProxy *rmn_proxy_contract.RMNProxyContract NonceManager *nonce_manager.NonceManager TokenAdminRegistry *token_admin_registry.TokenAdminRegistry RegistryModule *registry_module_owner_custom.RegistryModuleOwnerCustom Router *router.Router - CommitStore *commit_store.CommitStore Weth9 *weth9.WETH9 RMNRemote *rmn_remote.RMNRemote - MockRMN *mock_rmn_contract.MockRMNContract // Map between token Descriptor (e.g. LinkSymbol, WethSymbol) // and the respective token contract // This is more of an illustration of how we'll have tokens, and it might need some work later to work properly. @@ -127,6 +126,9 @@ type CCIPChainState struct { MockUSDCTransmitter *mock_usdc_token_transmitter.MockE2EUSDCTransmitter MockUSDCTokenMessenger *mock_usdc_token_messenger.MockE2EUSDCTokenMessenger Multicall3 *multicall3.Multicall3 + + // Legacy contracts + legacychangeset.CCIPChainStateLegacy } func (c CCIPChainState) GenerateView() (view.ChainView, error) { @@ -190,20 +192,12 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) { chainView.OffRamp[c.OffRamp.Address().Hex()] = offRampView } - if c.CommitStore != nil { - commitStoreView, err := v1_5.GenerateCommitStoreView(c.CommitStore) - if err != nil { - return chainView, errors.Wrapf(err, "failed to generate commit store view for commit store %s", c.CommitStore.Address().String()) - } - chainView.CommitStore[c.CommitStore.Address().Hex()] = commitStoreView - } - - if c.RMNProxyNew != nil { - rmnProxyView, err := v1_0.GenerateRMNProxyView(c.RMNProxyNew) + if c.RMNProxy != nil { + rmnProxyView, err := v1_0.GenerateRMNProxyView(c.RMNProxy) if err != nil { - return chainView, errors.Wrapf(err, "failed to generate rmn proxy view for rmn proxy %s", c.RMNProxyNew.Address().String()) + return chainView, errors.Wrapf(err, "failed to generate rmn proxy view for rmn proxy %s", c.RMNProxy.Address().String()) } - chainView.RMNProxy[c.RMNProxyNew.Address().Hex()] = rmnProxyView + chainView.RMNProxy[c.RMNProxy.Address().Hex()] = rmnProxyView } if c.CapabilityRegistry != nil { capRegView, err := common_v1_0.GenerateCapabilityRegistryView(c.CapabilityRegistry) @@ -233,6 +227,17 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) { } chainView.StaticLinkToken = staticLinkTokenView } + // Legacy contracts + if c.CommitStore != nil { + for _, commitStore := range c.CommitStore { + commitStoreView, err := v1_5.GenerateCommitStoreView(commitStore) + if err != nil { + return chainView, errors.Wrapf(err, "failed to generate commit store view for commit store %s", commitStore.Address().String()) + } + chainView.CommitStore[commitStore.Address().Hex()] = commitStoreView + } + } + return chainView, nil } @@ -341,25 +346,7 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type if err != nil { return state, err } - state.RMNProxyExisting = armProxy - case deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_6_0_dev).String(): - armProxy, err := rmn_proxy_contract.NewRMNProxyContract(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.RMNProxyNew = armProxy - case deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_6_0_dev).String(): - armProxy, err := rmn_proxy_contract.NewRMNProxyContract(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.RMNProxyNew = armProxy - case deployment.NewTypeAndVersion(MockRMN, deployment.Version1_0_0).String(): - mockRMN, err := mock_rmn_contract.NewMockRMNContract(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.MockRMN = mockRMN + state.RMNProxy = armProxy case deployment.NewTypeAndVersion(RMNRemote, deployment.Version1_6_0_dev).String(): rmnRemote, err := rmn_remote.NewRMNRemote(common.HexToAddress(address), chain.Client) if err != nil { @@ -384,12 +371,6 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type return state, err } state.NonceManager = nm - case deployment.NewTypeAndVersion(CommitStore, deployment.Version1_5_0).String(): - cs, err := commit_store.NewCommitStore(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.CommitStore = cs case deployment.NewTypeAndVersion(TokenAdminRegistry, deployment.Version1_5_0).String(): tm, err := token_admin_registry.NewTokenAdminRegistry(common.HexToAddress(address), chain.Client) if err != nil { @@ -522,6 +503,64 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type return state, fmt.Errorf("failed to get token symbol of token at %s: %w", address, err) } state.BurnMintTokens677[TokenSymbol(symbol)] = tok + // legacy addresses below + case deployment.NewTypeAndVersion(OnRamp, deployment.Version1_5_0).String(): + onRampC, err := evm_2_evm_onramp.NewEVM2EVMOnRamp(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + sCfg, err := onRampC.GetStaticConfig(nil) + if err != nil { + return state, fmt.Errorf("failed to get static config chain %s: %w", chain.String(), err) + } + if state.EVM2EVMOnRamp == nil { + state.EVM2EVMOnRamp = make(map[uint64]*evm_2_evm_onramp.EVM2EVMOnRamp) + } + state.EVM2EVMOnRamp[sCfg.DestChainSelector] = onRampC + case deployment.NewTypeAndVersion(OffRamp, deployment.Version1_5_0).String(): + offRamp, err := evm_2_evm_offramp.NewEVM2EVMOffRamp(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + sCfg, err := offRamp.GetStaticConfig(nil) + if err != nil { + return state, err + } + if state.EVM2EVMOffRamp == nil { + state.EVM2EVMOffRamp = make(map[uint64]*evm_2_evm_offramp.EVM2EVMOffRamp) + } + state.EVM2EVMOffRamp[sCfg.SourceChainSelector] = offRamp + case deployment.NewTypeAndVersion(CommitStore, deployment.Version1_5_0).String(): + commitStore, err := commit_store.NewCommitStore(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + sCfg, err := commitStore.GetStaticConfig(nil) + if err != nil { + return state, err + } + if state.CommitStore == nil { + state.CommitStore = make(map[uint64]*commit_store.CommitStore) + } + state.CommitStore[sCfg.SourceChainSelector] = commitStore + case deployment.NewTypeAndVersion(PriceRegistry, deployment.Version1_2_0).String(): + pr, err := price_registry_1_2_0.NewPriceRegistry(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.PriceRegistry = pr + case deployment.NewTypeAndVersion(RMN, deployment.Version1_5_0).String(): + rmnC, err := rmn_contract.NewRMNContract(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.RMN = rmnC + case deployment.NewTypeAndVersion(MockRMN, deployment.Version1_0_0).String(): + mockRMN, err := mock_rmn_contract.NewMockRMNContract(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.MockRMN = mockRMN default: return state, fmt.Errorf("unknown contract %s", tvStr) } diff --git a/deployment/ccip/changeset/v1_5/cs_deploy_chains.go b/deployment/ccip/changeset/v1_5/cs_deploy_chains.go index 9d4cc107cf0..8855232c449 100644 --- a/deployment/ccip/changeset/v1_5/cs_deploy_chains.go +++ b/deployment/ccip/changeset/v1_5/cs_deploy_chains.go @@ -3,23 +3,32 @@ package v1_5 import ( "fmt" + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" ) -var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContracts +var _ deployment.ChangeSet[DeployChainContractsConfig1_5] = DeployChainContracts -type DeployChainContractsConfig struct { - ChainSelectors []uint64 +type DeployChainContractsConfig1_5 struct { + configs []DeployChainContractsConfig1_5PerChain } -func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig) (deployment.ChangesetOutput, error) { +type DeployChainContractsConfig1_5PerChain struct { + ChainSelector uint64 + RMNConfig *rmn_contract.RMNConfig +} + +func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig1_5) (deployment.ChangesetOutput, error) { if err := c.Validate(); err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig: %w", err) + return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig1_5: %w", err) } newAddresses := deployment.NewMemoryAddressBook() - err := deployChainContractsForChains(env, newAddresses, c.ChainSelectors) + err := deployChainContractsForChains(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) @@ -31,15 +40,89 @@ func DeployChainContracts(env deployment.Environment, c DeployChainContractsConf }, nil } -func (c DeployChainContractsConfig) Validate() error { - for _, cs := range c.ChainSelectors { - if err := deployment.IsValidChainSelector(cs); err != nil { +func (c DeployChainContractsConfig1_5) Validate() error { + for _, cs := range c.configs { + if err := deployment.IsValidChainSelector(cs.ChainSelector); err != nil { return fmt.Errorf("invalid chain selector: %d - %w", cs, err) } } return nil } -func deployChainContractsForChains(env deployment.Environment, ab deployment.AddressBook, selectors []uint64) error { +func deployChainContractsForChains(env deployment.Environment, ab deployment.AddressBook, cfg DeployChainContractsConfig1_5) error { + state, err := changeset.LoadOnchainState(env) + if err != nil { + return fmt.Errorf("failed to load onchain state: %w", err) + } + for _, c := range cfg.configs { + if err := deployChainContracts(env, ab, state, c); err != nil { + return fmt.Errorf("failed to deploy CCIP contracts for chain %d: %w", c.ChainSelector, err) + } + } + return nil +} + +func deployChainContracts( + env deployment.Environment, + ab deployment.AddressBook, + state changeset.CCIPOnChainState, + cfg DeployChainContractsConfig1_5PerChain) error { + chain, ok := env.Chains[cfg.ChainSelector] + if !ok { + return fmt.Errorf("chain %d not found", cfg.ChainSelector) + } + chainState, ok := state.Chains[cfg.ChainSelector] + if !ok { + return fmt.Errorf("chain %d state not found", cfg.ChainSelector) + } + lggr := env.Logger + var rmnAddr common.Address + if cfg.RMNConfig == nil { + if chainState.MockRMN == nil { + rmn, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract] { + rmnAddr, tx2, rmn, err2 := mock_rmn_contract.DeployMockRMNContract( + chain.DeployerKey, + chain.Client, + ) + return deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract]{ + rmnAddr, rmn, tx2, deployment.NewTypeAndVersion(changeset.MockRMN, deployment.Version1_0_0), err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy mock RMN", "chain", chain.String(), "err", err) + return err + } + rmnAddr = rmn.Address + } else { + lggr.Infow("MockRMN already deployed", "chain", chain.String(), "address", chainState.MockRMN.Address()) + rmnAddr = chainState.MockRMN.Address() + } + } else { + if chainState.RMN == nil { + rmn, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*rmn_contract.RMNContract] { + rmnAddr, tx2, rmn, err2 := rmn_contract.DeployRMNContract( + chain.DeployerKey, + chain.Client, + *cfg.RMNConfig, + ) + return deployment.ContractDeploy[*rmn_contract.RMNContract]{ + rmnAddr, rmn, tx2, deployment.NewTypeAndVersion(changeset.RMN, deployment.Version1_5_0), err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy RMN", "chain", chain.String(), "err", err) + return err + } + rmnAddr = rmn.Address + } else { + lggr.Infow("RMN already deployed", "chain", chain.String(), "address", chainState.RMN.Address) + rmnAddr = chainState.RMN.Address() + } + } + if chainState.RMNProxy == nil { + } + return nil } diff --git a/deployment/ccip/changeset/v1_5/state.go b/deployment/ccip/changeset/v1_5/state.go index 338301fb708..29649a0db81 100644 --- a/deployment/ccip/changeset/v1_5/state.go +++ b/deployment/ccip/changeset/v1_5/state.go @@ -1,320 +1,20 @@ package v1_5 import ( - "fmt" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - - "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" - commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/burn_mint_token_pool" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_home" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" - "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/nonce_manager" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_proxy_contract" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_remote" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/token_admin_registry" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/usdc_token_pool" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/weth9" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/multicall3" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" ) -// CCIPOnChainState_Legacy state always derivable from an address book. -// Offchain state always derivable from a list of nodeIds. -// Note can translate this into Go struct needed for MCMS/Docs/UI. -type CCIPOnChainState_Legacy struct { - // Populated go bindings for the appropriate version for all contracts. - // We would hold 2 versions of each contract here. Once we upgrade we can phase out the old one. - // When generating bindings, make sure the package name corresponds to the version. - Chains map[uint64]CCIPChainState -} - -// 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 - commoncs.LinkTokenState - commoncs.StaticLinkTokenState - OnRamp map[uint64]*evm_2_evm_onramp.EVM2EVMOnRamp - CommitStore map[uint64]*commit_store.CommitStore - OffRamp map[uint64]*evm_2_evm_offramp.EVM2EVMOffRamp - RMNProxy *rmn_proxy_contract.RMNProxyContract - TokenAdminRegistry *token_admin_registry.TokenAdminRegistry - RegistryModule *registry_module_owner_custom.RegistryModuleOwnerCustom - FeeQuoter *fee_quoter.FeeQuoter - Router *router.Router - Weth9 *weth9.WETH9 - MockRMN *mock_rmn_contract.MockRMNContract - // Map between token Descriptor (e.g. LinkSymbol, WethSymbol) - // and the respective token contract - // This is more of an illustration of how we'll have tokens, and it might need some work later to work properly. - // Not all tokens will be burn and mint tokens. - BurnMintTokens677 map[changeset.TokenSymbol]*burn_mint_erc677.BurnMintERC677 - BurnMintTokenPools map[changeset.TokenSymbol]*burn_mint_token_pool.BurnMintTokenPool - // Map between token Symbol (e.g. LinkSymbol, WethSymbol) - // and the respective aggregator USD feed contract - USDFeeds map[changeset.TokenSymbol]*aggregator_v3_interface.AggregatorV3Interface - - // Test contracts - Receiver *maybe_revert_message_receiver.MaybeRevertMessageReceiver - TestRouter *router.Router - USDCTokenPool *usdc_token_pool.USDCTokenPool - MockUSDCTransmitter *mock_usdc_token_transmitter.MockE2EUSDCTransmitter - MockUSDCTokenMessenger *mock_usdc_token_messenger.MockE2EUSDCTokenMessenger -} - -func LoadChainState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (CCIPChainState, error) { - var state CCIPChainState - mcmsWithTimelock, err := commoncs.MaybeLoadMCMSWithTimelockState(chain, addresses) - if err != nil { - return state, err - } - state.MCMSWithTimelockState = *mcmsWithTimelock - - linkState, err := commoncs.MaybeLoadLinkTokenState(chain, addresses) - if err != nil { - return state, err - } - state.LinkTokenState = *linkState - staticLinkState, err := commoncs.MaybeLoadStaticLinkTokenState(chain, addresses) - if err != nil { - return state, err - } - state.StaticLinkTokenState = *staticLinkState - 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(), - deployment.NewTypeAndVersion(commontypes.LinkToken, deployment.Version1_0_0).String(), - deployment.NewTypeAndVersion(commontypes.StaticLinkToken, deployment.Version1_0_0).String(): - // Skip common contracts, they are already loaded. - continue - case deployment.NewTypeAndVersion(changeset.OnRamp, deployment.Version1_5_0).String(): - onRampC, err := evm_2_evm_onramp.NewEVM2EVMOnRamp(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - sCfg, err := onRampC.GetStaticConfig(nil) - if err != nil { - return state, err - } - if state.OnRamp == nil { - state.OnRamp = make(map[uint64]*evm_2_evm_onramp.EVM2EVMOnRamp) - } - state.OnRamp[sCfg.DestChainSelector] = onRampC - case deployment.NewTypeAndVersion(changeset.OffRamp, deployment.Version1_5_0).String(): - offRamp, err := evm(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.OffRamp = offRamp - case deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_0_0).String(): - armProxy, err := rmn_proxy_contract.NewRMNProxyContract(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.RMNProxyExisting = armProxy - case deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_6_0_dev).String(): - armProxy, err := rmn_proxy_contract.NewRMNProxyContract(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.RMNProxyNew = armProxy - case deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_6_0_dev).String(): - armProxy, err := rmn_proxy_contract.NewRMNProxyContract(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.RMNProxyNew = armProxy - case deployment.NewTypeAndVersion(MockRMN, deployment.Version1_0_0).String(): - mockRMN, err := mock_rmn_contract.NewMockRMNContract(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.MockRMN = mockRMN - case deployment.NewTypeAndVersion(RMNRemote, deployment.Version1_6_0_dev).String(): - rmnRemote, err := rmn_remote.NewRMNRemote(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.RMNRemote = rmnRemote - case deployment.NewTypeAndVersion(RMNHome, deployment.Version1_6_0_dev).String(): - rmnHome, err := rmn_home.NewRMNHome(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.RMNHome = rmnHome - case deployment.NewTypeAndVersion(WETH9, deployment.Version1_0_0).String(): - weth9, err := weth9.NewWETH9(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.Weth9 = weth9 - case deployment.NewTypeAndVersion(NonceManager, deployment.Version1_6_0_dev).String(): - nm, err := nonce_manager.NewNonceManager(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.NonceManager = nm - case deployment.NewTypeAndVersion(CommitStore, deployment.Version1_5_0).String(): - cs, err := commit_store.NewCommitStore(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.CommitStore = cs - case deployment.NewTypeAndVersion(TokenAdminRegistry, deployment.Version1_5_0).String(): - tm, err := token_admin_registry.NewTokenAdminRegistry(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.TokenAdminRegistry = tm - case deployment.NewTypeAndVersion(RegistryModule, deployment.Version1_5_0).String(): - rm, err := registry_module_owner_custom.NewRegistryModuleOwnerCustom(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.RegistryModule = rm - case deployment.NewTypeAndVersion(Router, deployment.Version1_2_0).String(): - r, err := router.NewRouter(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.Router = r - case deployment.NewTypeAndVersion(TestRouter, deployment.Version1_2_0).String(): - r, err := router.NewRouter(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.TestRouter = r - case deployment.NewTypeAndVersion(FeeQuoter, deployment.Version1_6_0_dev).String(): - fq, err := fee_quoter.NewFeeQuoter(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.FeeQuoter = fq - case deployment.NewTypeAndVersion(USDCToken, deployment.Version1_0_0).String(): - ut, err := burn_mint_erc677.NewBurnMintERC677(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.BurnMintTokens677 = map[TokenSymbol]*burn_mint_erc677.BurnMintERC677{ - USDCSymbol: ut, - } - case deployment.NewTypeAndVersion(USDCTokenPool, deployment.Version1_0_0).String(): - utp, err := usdc_token_pool.NewUSDCTokenPool(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.USDCTokenPool = utp - case deployment.NewTypeAndVersion(USDCMockTransmitter, deployment.Version1_0_0).String(): - umt, err := mock_usdc_token_transmitter.NewMockE2EUSDCTransmitter(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.MockUSDCTransmitter = umt - case deployment.NewTypeAndVersion(USDCTokenMessenger, deployment.Version1_0_0).String(): - utm, err := mock_usdc_token_messenger.NewMockE2EUSDCTokenMessenger(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.MockUSDCTokenMessenger = utm - case deployment.NewTypeAndVersion(CCIPHome, deployment.Version1_6_0_dev).String(): - ccipHome, err := ccip_home.NewCCIPHome(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.CCIPHome = ccipHome - case deployment.NewTypeAndVersion(CCIPConfig, deployment.Version1_0_0).String(): - // TODO: Remove once staging upgraded. - ccipConfig, err := ccip_config.NewCCIPConfig(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.CCIPConfig = ccipConfig - case deployment.NewTypeAndVersion(CCIPReceiver, deployment.Version1_0_0).String(): - mr, err := maybe_revert_message_receiver.NewMaybeRevertMessageReceiver(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.Receiver = mr - case deployment.NewTypeAndVersion(Multicall3, deployment.Version1_0_0).String(): - mc, err := multicall3.NewMulticall3(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - state.Multicall3 = mc - case deployment.NewTypeAndVersion(PriceFeed, deployment.Version1_0_0).String(): - feed, err := aggregator_v3_interface.NewAggregatorV3Interface(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - if state.USDFeeds == nil { - state.USDFeeds = make(map[TokenSymbol]*aggregator_v3_interface.AggregatorV3Interface) - } - desc, err := feed.Description(&bind.CallOpts{}) - if err != nil { - return state, err - } - key, ok := MockDescriptionToTokenSymbol[desc] - if !ok { - return state, fmt.Errorf("unknown feed description %s", desc) - } - state.USDFeeds[key] = feed - case deployment.NewTypeAndVersion(BurnMintTokenPool, deployment.Version1_5_1).String(): - pool, err := burn_mint_token_pool.NewBurnMintTokenPool(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - if state.BurnMintTokenPools == nil { - state.BurnMintTokenPools = make(map[TokenSymbol]*burn_mint_token_pool.BurnMintTokenPool) - } - tokAddress, err := pool.GetToken(nil) - if err != nil { - return state, err - } - tok, err := erc20.NewERC20(tokAddress, chain.Client) - if err != nil { - return state, err - } - symbol, err := tok.Symbol(nil) - if err != nil { - return state, err - } - state.BurnMintTokenPools[TokenSymbol(symbol)] = pool - case deployment.NewTypeAndVersion(BurnMintToken, deployment.Version1_0_0).String(): - tok, err := burn_mint_erc677.NewBurnMintERC677(common.HexToAddress(address), chain.Client) - if err != nil { - return state, err - } - if state.BurnMintTokens677 == nil { - state.BurnMintTokens677 = make(map[TokenSymbol]*burn_mint_erc677.BurnMintERC677) - } - symbol, err := tok.Symbol(nil) - if err != nil { - return state, fmt.Errorf("failed to get token symbol of token at %s: %w", address, err) - } - state.BurnMintTokens677[TokenSymbol(symbol)] = tok - default: - return state, fmt.Errorf("unknown contract %s", tvStr) - } - } - return state, nil +// CCIPChainStateLegacy holds 1.5 state for CCIP chains +type CCIPChainStateLegacy struct { + EVM2EVMOnRamp map[uint64]*evm_2_evm_onramp.EVM2EVMOnRamp + CommitStore map[uint64]*commit_store.CommitStore + EVM2EVMOffRamp map[uint64]*evm_2_evm_offramp.EVM2EVMOffRamp + MockRMN *mock_rmn_contract.MockRMNContract + PriceRegistry *price_registry_1_2_0.PriceRegistry + RMN *rmn_contract.RMNContract } From 4a143bfae387b0ef593ca5fa715914343b8a45b0 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 10 Dec 2024 17:09:41 -0800 Subject: [PATCH 06/29] updates --- .../ccip/changeset/v1_5/cs_deploy_chains.go | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/deployment/ccip/changeset/v1_5/cs_deploy_chains.go b/deployment/ccip/changeset/v1_5/cs_deploy_chains.go index 8855232c449..96177a59144 100644 --- a/deployment/ccip/changeset/v1_5/cs_deploy_chains.go +++ b/deployment/ccip/changeset/v1_5/cs_deploy_chains.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_proxy_contract" ) var _ deployment.ChangeSet[DeployChainContractsConfig1_5] = DeployChainContracts @@ -122,7 +123,47 @@ func deployChainContracts( } } if chainState.RMNProxy == nil { + _, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract] { + rmnProxyAddr, tx2, rmnProxy, err2 := rmn_proxy_contract.DeployRMNProxyContract( + chain.DeployerKey, + chain.Client, + rmnAddr, + ) + return deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract]{ + rmnProxyAddr, rmnProxy, tx2, deployment.NewTypeAndVersion(changeset.ARMProxy, deployment.Version1_0_0), err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy RMNProxy", "chain", chain.String(), "err", err) + return err + } + } else { + lggr.Infow("RMNProxy already deployed", "chain", chain.String(), "address", chainState.RMNProxy.Address) + // check if the RMNProxy is pointing to the correct RMN + rmnProxy := chainState.RMNProxy + setRMN, err := rmnProxy.GetARM(nil) + if err != nil { + return err + } + if setRMN != rmnAddr { + lggr.Infow("RMNProxy pointing to wrong RMN, changing the RMN", "chain", chain.String(), "rmnProxy", rmnProxy.Address, "rmn", rmnAddr) + tx, err := rmnProxy.SetARM(chain.DeployerKey, rmnAddr) + if err != nil { + return fmt.Errorf("failed to set RMN on RMNProxy for chain %s RMN %s RMNProxy %s: %w", + chain.String(), rmnAddr.String(), rmnProxy.Address().String(), err) + } + _, err = chain.Confirm(tx) + if err != nil { + lggr.Errorw("Failed to confirm RMNProxy SetARM", + "chain", chain.String(), "rmn", rmnAddr.String(), "rmnProxy", rmnProxy.Address().String(), "err", err) + return fmt.Errorf("failed to confirm RMNProxy SetARM for chain %s RMN %s RMNProxy %s: %w", + chain.String(), rmnAddr.String(), rmnProxy.Address().String(), err) + } + lggr.Infow("RMNProxy SetARM confirmed", + "chain", chain.String(), "rmn", rmnAddr.String(), "rmnProxy", rmnProxy.Address().String()) + } } return nil } From 7aaa0ef946897cbfa64c095b5153b3acef8e531d Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 11 Dec 2024 12:24:47 -0800 Subject: [PATCH 07/29] all updates --- deployment/ccip/changeset/cs_deploy_chain.go | 2 +- deployment/ccip/changeset/cs_prerequisites.go | 2 +- .../ccip/changeset/v1_5/cs_add_lanes.go | 286 ++++++++++++++++++ .../ccip/changeset/v1_5/cs_deploy_chains.go | 213 ++++++++++++- 4 files changed, 486 insertions(+), 17 deletions(-) create mode 100644 deployment/ccip/changeset/v1_5/cs_add_lanes.go diff --git a/deployment/ccip/changeset/cs_deploy_chain.go b/deployment/ccip/changeset/cs_deploy_chain.go index e9e9d03f8c9..68b33004224 100644 --- a/deployment/ccip/changeset/cs_deploy_chain.go +++ b/deployment/ccip/changeset/cs_deploy_chain.go @@ -233,7 +233,7 @@ func deployChainContracts( } }) if err != nil { - e.Logger.Errorw("Failed to deploy receiver", "err", err) + e.Logger.Errorw("Failed to deploy receiver", "chain", chain.String(), "err", err) return err } } else { diff --git a/deployment/ccip/changeset/cs_prerequisites.go b/deployment/ccip/changeset/cs_prerequisites.go index e98697ed8d6..883fa1b1db6 100644 --- a/deployment/ccip/changeset/cs_prerequisites.go +++ b/deployment/ccip/changeset/cs_prerequisites.go @@ -246,7 +246,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } weth9Contract = weth.Contract } else { - lggr.Infow("weth9 already deployed", "addr", weth9Contract.Address) + lggr.Infow("weth9 already deployed", "chain", chain.String(), "addr", weth9Contract.Address) } // if router is not already deployed, we deploy it if r == nil { diff --git a/deployment/ccip/changeset/v1_5/cs_add_lanes.go b/deployment/ccip/changeset/v1_5/cs_add_lanes.go new file mode 100644 index 00000000000..78a68cafc6e --- /dev/null +++ b/deployment/ccip/changeset/v1_5/cs_add_lanes.go @@ -0,0 +1,286 @@ +package v1_5 + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" +) + +type AddLanesConfig struct { + Configs []AddLaneConfig +} + +func (c *AddLanesConfig) Validate(e deployment.Environment, state changeset.CCIPOnChainState) error { + for _, cfg := range c.Configs { + if err := cfg.Validate(e, state); err != nil { + return err + } + } + return nil +} + +type AddLaneConfig struct { + SourceChainSelector uint64 + DestinationChainSelector uint64 + + // onRamp specific configuration + OnRampStaticCfg evm_2_evm_onramp.EVM2EVMOnRampStaticConfig + OnRampDynamicCfg evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig + OnRampFeeTokenArgs []evm_2_evm_onramp.EVM2EVMOnRampFeeTokenConfigArgs + OnRampTransferTokenCfgs []evm_2_evm_onramp.EVM2EVMOnRampTokenTransferFeeConfigArgs + OnRampNopsAndWeight []evm_2_evm_onramp.EVM2EVMOnRampNopAndWeight + OnRampRateLimiterCfg evm_2_evm_onramp.RateLimiterConfig + + // offRamp specific configuration + OffRampRateLimiterCfg evm_2_evm_offramp.RateLimiterConfig + + // Price Registry specific configuration + InitialTokenPrices []price_registry_1_2_0.InternalTokenPriceUpdate + GasPriceUpdates []price_registry_1_2_0.InternalGasPriceUpdate +} + +func (c *AddLaneConfig) Validate(e deployment.Environment, state changeset.CCIPOnChainState) error { + if err := deployment.IsValidChainSelector(c.SourceChainSelector); err != nil { + return err + } + if err := deployment.IsValidChainSelector(c.DestinationChainSelector); err != nil { + return err + } + sourceChain, exists := e.Chains[c.SourceChainSelector] + if !exists { + return fmt.Errorf("source chain %d not found in environment", c.SourceChainSelector) + } + destChain, exists := e.Chains[c.DestinationChainSelector] + if !exists { + return fmt.Errorf("destination chain %d not found in environment", c.DestinationChainSelector) + } + sourceChainState, exists := state.Chains[c.SourceChainSelector] + if !exists { + return fmt.Errorf("source chain %d not found in state", c.SourceChainSelector) + } + destChainState, exists := state.Chains[c.DestinationChainSelector] + if !exists { + return fmt.Errorf("destination chain %d not found in state", c.DestinationChainSelector) + } + // check for existing chain contracts on both source and destination chains + if err := arePrerequisitesMet(sourceChainState, sourceChain); err != nil { + return err + } + if err := arePrerequisitesMet(destChainState, destChain); err != nil { + return err + } + // TODO: Add rest of the config validation + return nil +} + +func AddLanes(env deployment.Environment, c AddLanesConfig) (deployment.ChangesetOutput, error) { + state, err := changeset.LoadOnchainState(env) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to load CCIP onchain state: %w", err) + } + if err := c.Validate(env, state); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig: %w", err) + } + newAddresses := deployment.NewMemoryAddressBook() + for _, cfg := range c.Configs { + if err := addLane(env, state, newAddresses, cfg); err != nil { + return deployment.ChangesetOutput{ + AddressBook: newAddresses, + }, err + } + } + return deployment.ChangesetOutput{ + AddressBook: newAddresses, + }, nil +} + +func addLane(e deployment.Environment, state changeset.CCIPOnChainState, ab deployment.AddressBook, cfg AddLaneConfig) error { + // update prices on the source price registry + sourceChainState := state.Chains[cfg.SourceChainSelector] + destChainState := state.Chains[cfg.DestinationChainSelector] + sourceChain := e.Chains[cfg.SourceChainSelector] + destChain := e.Chains[cfg.DestinationChainSelector] + sourcePriceReg := sourceChainState.PriceRegistry + tx, err := sourcePriceReg.UpdatePrices(sourceChain.DeployerKey, price_registry_1_2_0.InternalPriceUpdates{ + TokenPriceUpdates: cfg.InitialTokenPrices, + GasPriceUpdates: cfg.GasPriceUpdates, + }) + if err != nil { + return err + } + _, err = sourceChain.Confirm(tx) + if err != nil { + return fmt.Errorf("failed to confirm price update tx for chain %s: %w", sourceChain.String(), deployment.MaybeDataErr(err)) + } + // ================================================================ + // │ Deploy Lane │ + // ================================================================ + // Deploy onRamp on source chain + onRamp, onRampExists := sourceChainState.EVM2EVMOnRamp[cfg.DestinationChainSelector] + if !onRampExists { + onRampC, err := deployment.DeployContract(e.Logger, sourceChain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*evm_2_evm_onramp.EVM2EVMOnRamp] { + onRampAddress, tx, onRampC, err2 := evm_2_evm_onramp.DeployEVM2EVMOnRamp( + sourceChain.DeployerKey, + sourceChain.Client, + cfg.OnRampStaticCfg, + cfg.OnRampDynamicCfg, + cfg.OnRampRateLimiterCfg, + cfg.OnRampFeeTokenArgs, + cfg.OnRampTransferTokenCfgs, + cfg.OnRampNopsAndWeight, + ) + return deployment.ContractDeploy[*evm_2_evm_onramp.EVM2EVMOnRamp]{ + Address: onRampAddress, Contract: onRampC, Tx: tx, + Tv: deployment.NewTypeAndVersion(changeset.OnRamp, deployment.Version1_5_0), Err: err2, + } + }) + if err != nil { + e.Logger.Errorw("Failed to deploy EVM2EVMOnRamp", "chain", sourceChain.String(), "err", err) + return err + } + onRamp = onRampC.Contract + } else { + e.Logger.Infow("EVM2EVMOnRamp already exists", + "source chain", sourceChain.String(), "destination chain", destChain.String(), + "address", onRamp.Address().String()) + } + + // Deploy commit store on source chain + commitStore, commitStoreExists := destChainState.CommitStore[cfg.SourceChainSelector] + if !commitStoreExists { + commitStoreC, err := deployment.DeployContract(e.Logger, sourceChain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*commit_store.CommitStore] { + commitStoreAddress, tx, commitStoreC, err2 := commit_store.DeployCommitStore( + destChain.DeployerKey, + destChain.Client, + commit_store.CommitStoreStaticConfig{ + ChainSelector: destChain.Selector, + SourceChainSelector: sourceChain.Selector, + OnRamp: onRamp.Address(), + RmnProxy: destChainState.RMNProxy.Address(), + }, + ) + return deployment.ContractDeploy[*commit_store.CommitStore]{ + Address: commitStoreAddress, Contract: commitStoreC, Tx: tx, + Tv: deployment.NewTypeAndVersion(changeset.CommitStore, deployment.Version1_5_0), Err: err2, + } + }) + if err != nil { + e.Logger.Errorw("Failed to deploy CommitStore", "chain", sourceChain.String(), "err", err) + return err + } + commitStore = commitStoreC.Contract + } else { + e.Logger.Infow("CommitStore already exists", + "source chain", sourceChain.String(), "destination chain", destChain.String(), + "address", commitStore.Address().String()) + } + + // Deploy offRamp on destination chain + offRamp, offRampExists := destChainState.EVM2EVMOffRamp[cfg.SourceChainSelector] + if !offRampExists { + offRampC, err := deployment.DeployContract(e.Logger, sourceChain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*evm_2_evm_offramp.EVM2EVMOffRamp] { + offRampAddress, tx, offRampC, err2 := evm_2_evm_offramp.DeployEVM2EVMOffRamp( + destChain.DeployerKey, + destChain.Client, + evm_2_evm_offramp.EVM2EVMOffRampStaticConfig{ + CommitStore: commitStore.Address(), + ChainSelector: destChain.Selector, + SourceChainSelector: sourceChain.Selector, + OnRamp: onRamp.Address(), + PrevOffRamp: common.HexToAddress(""), + RmnProxy: destChainState.RMNProxy.Address(), // RMN, formerly ARM + TokenAdminRegistry: destChainState.TokenAdminRegistry.Address(), + }, + cfg.OffRampRateLimiterCfg, + ) + return deployment.ContractDeploy[*evm_2_evm_offramp.EVM2EVMOffRamp]{ + Address: offRampAddress, Contract: offRampC, Tx: tx, + Tv: deployment.NewTypeAndVersion(changeset.OffRamp, deployment.Version1_5_0), Err: err2, + } + }) + if err != nil { + e.Logger.Errorw("Failed to deploy EVM2EVMOffRamp", "chain", sourceChain.String(), "err", err) + return err + } + offRamp = offRampC.Contract + } else { + e.Logger.Infow("EVM2EVMOffRamp already exists", + "source chain", sourceChain.String(), "destination chain", destChain.String(), + "address", offRamp.Address().String()) + } + + // Apply Router updates + tx, err = sourceChainState.Router.ApplyRampUpdates(sourceChain.DeployerKey, + []router.RouterOnRamp{{DestChainSelector: destChain.Selector, OnRamp: onRamp.Address()}}, nil, nil) + if err != nil { + return fmt.Errorf("failed to apply router updates for source chain %s: %w", sourceChain.String(), deployment.MaybeDataErr(err)) + } + _, err = sourceChain.Confirm(tx) + if err != nil { + return fmt.Errorf("failed to confirm router updates tx %s for source chain %s: %w", tx.Hash().String(), sourceChain.String(), deployment.MaybeDataErr(err)) + } + + tx, err = destChainState.Router.ApplyRampUpdates(destChain.DeployerKey, + nil, + nil, + []router.RouterOffRamp{{SourceChainSelector: sourceChain.Selector, OffRamp: offRamp.Address()}}, + ) + if err != nil { + return fmt.Errorf("failed to apply router updates for destination chain %s: %w", destChain.String(), deployment.MaybeDataErr(err)) + } + _, err = destChain.Confirm(tx) + if err != nil { + return fmt.Errorf("failed to confirm router updates tx %s for destination chain %s: %w", tx.Hash().String(), destChain.String(), deployment.MaybeDataErr(err)) + } + + // price registry updates + _, err = destChainState.PriceRegistry.ApplyPriceUpdatersUpdates( + destChain.DeployerKey, + []common.Address{commitStore.Address()}, + []common.Address{}, + ) + if err != nil { + return fmt.Errorf("failed to apply price registry updates for destination chain %s: %w", destChain.String(), deployment.MaybeDataErr(err)) + } + _, err = destChain.Confirm(tx) + if err != nil { + return fmt.Errorf("failed to confirm price registry updates tx %s for destination chain %s: %w", tx.Hash().String(), destChain.String(), deployment.MaybeDataErr(err)) + } + return nil +} + +func arePrerequisitesMet(chainState changeset.CCIPChainState, chain deployment.Chain) error { + if chainState.Router == nil { + return fmt.Errorf("router not found for chain %s", chain.String()) + } + if chainState.PriceRegistry == nil { + return fmt.Errorf("price registry not found for chain %s", chain.String()) + } + if chainState.RMN == nil && chainState.MockRMN == nil { + return fmt.Errorf("neither RMN nor mockRMN found for chain %s", chain.String()) + } + if chainState.Weth9 == nil { + return fmt.Errorf("WETH9 not found for chain %s", chain.String()) + } + if chainState.LinkToken == nil { + return fmt.Errorf("LINK token not found for chain %s", chain.String()) + } + if chainState.TokenAdminRegistry == nil { + return fmt.Errorf("token admin registry not found for chain %s", chain.String()) + } + if chainState.RMNProxy == nil { + return fmt.Errorf("RMNProxy not found for chain %s", chain.String()) + } + return nil +} diff --git a/deployment/ccip/changeset/v1_5/cs_deploy_chains.go b/deployment/ccip/changeset/v1_5/cs_deploy_chains.go index 96177a59144..ebd33d423f4 100644 --- a/deployment/ccip/changeset/v1_5/cs_deploy_chains.go +++ b/deployment/ccip/changeset/v1_5/cs_deploy_chains.go @@ -8,25 +8,32 @@ import ( "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_proxy_contract" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/token_admin_registry" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/weth9" ) -var _ deployment.ChangeSet[DeployChainContractsConfig1_5] = DeployChainContracts +var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContracts -type DeployChainContractsConfig1_5 struct { - configs []DeployChainContractsConfig1_5PerChain +type DeployChainContractsConfig struct { + configs []DeployChainContractsConfigPerChain } -type DeployChainContractsConfig1_5PerChain struct { - ChainSelector uint64 - RMNConfig *rmn_contract.RMNConfig +type DeployChainContractsConfigPerChain struct { + ChainSelector uint64 + RMNConfig *rmn_contract.RMNConfig + PriceRegStalenessThreshold uint32 } -func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig1_5) (deployment.ChangesetOutput, error) { +func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig) (deployment.ChangesetOutput, error) { if err := c.Validate(); err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig1_5: %w", err) + return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig: %w", err) } newAddresses := deployment.NewMemoryAddressBook() err := deployChainContractsForChains(env, newAddresses, c) @@ -41,16 +48,17 @@ func DeployChainContracts(env deployment.Environment, c DeployChainContractsConf }, nil } -func (c DeployChainContractsConfig1_5) Validate() error { +func (c DeployChainContractsConfig) Validate() error { for _, cs := range c.configs { if err := deployment.IsValidChainSelector(cs.ChainSelector); err != nil { return fmt.Errorf("invalid chain selector: %d - %w", cs, err) } } + // TODO: Add rest of the config validation return nil } -func deployChainContractsForChains(env deployment.Environment, ab deployment.AddressBook, cfg DeployChainContractsConfig1_5) error { +func deployChainContractsForChains(env deployment.Environment, ab deployment.AddressBook, cfg DeployChainContractsConfig) error { state, err := changeset.LoadOnchainState(env) if err != nil { return fmt.Errorf("failed to load onchain state: %w", err) @@ -67,7 +75,7 @@ func deployChainContracts( env deployment.Environment, ab deployment.AddressBook, state changeset.CCIPOnChainState, - cfg DeployChainContractsConfig1_5PerChain) error { + cfg DeployChainContractsConfigPerChain) error { chain, ok := env.Chains[cfg.ChainSelector] if !ok { return fmt.Errorf("chain %d not found", cfg.ChainSelector) @@ -77,6 +85,9 @@ func deployChainContracts( return fmt.Errorf("chain %d state not found", cfg.ChainSelector) } lggr := env.Logger + // ================================================================ + // │ Deploy RMN │ + // ================================================================ var rmnAddr common.Address if cfg.RMNConfig == nil { if chainState.MockRMN == nil { @@ -87,7 +98,7 @@ func deployChainContracts( chain.Client, ) return deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract]{ - rmnAddr, rmn, tx2, deployment.NewTypeAndVersion(changeset.MockRMN, deployment.Version1_0_0), err2, + Address: rmnAddr, Contract: rmn, Tx: tx2, Tv: deployment.NewTypeAndVersion(changeset.MockRMN, deployment.Version1_0_0), Err: err2, } }) if err != nil { @@ -109,7 +120,7 @@ func deployChainContracts( *cfg.RMNConfig, ) return deployment.ContractDeploy[*rmn_contract.RMNContract]{ - rmnAddr, rmn, tx2, deployment.NewTypeAndVersion(changeset.RMN, deployment.Version1_5_0), err2, + Address: rmnAddr, Contract: rmn, Tx: tx2, Tv: deployment.NewTypeAndVersion(changeset.RMN, deployment.Version1_5_0), Err: err2, } }) if err != nil { @@ -122,8 +133,9 @@ func deployChainContracts( rmnAddr = chainState.RMN.Address() } } + var rmnProxyAddress common.Address if chainState.RMNProxy == nil { - _, err := deployment.DeployContract(lggr, chain, ab, + rmnProxy, err := deployment.DeployContract(lggr, chain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract] { rmnProxyAddr, tx2, rmnProxy, err2 := rmn_proxy_contract.DeployRMNProxyContract( chain.DeployerKey, @@ -131,13 +143,14 @@ func deployChainContracts( rmnAddr, ) return deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract]{ - rmnProxyAddr, rmnProxy, tx2, deployment.NewTypeAndVersion(changeset.ARMProxy, deployment.Version1_0_0), err2, + Address: rmnProxyAddr, Contract: rmnProxy, Tx: tx2, Tv: deployment.NewTypeAndVersion(changeset.ARMProxy, deployment.Version1_0_0), Err: err2, } }) if err != nil { lggr.Errorw("Failed to deploy RMNProxy", "chain", chain.String(), "err", err) return err } + rmnProxyAddress = rmnProxy.Address } else { lggr.Infow("RMNProxy already deployed", "chain", chain.String(), "address", chainState.RMNProxy.Address) // check if the RMNProxy is pointing to the correct RMN @@ -164,6 +177,176 @@ func deployChainContracts( lggr.Infow("RMNProxy SetARM confirmed", "chain", chain.String(), "rmn", rmnAddr.String(), "rmnProxy", rmnProxy.Address().String()) } + rmnProxyAddress = rmnProxy.Address() + } + // ================================================================ + // │ Deploy TokenAdminRegistry │ + // ================================================================ + var tokenAdminReg *token_admin_registry.TokenAdminRegistry + var registryModule *registry_module_owner_custom.RegistryModuleOwnerCustom + if chainState.TokenAdminRegistry == nil { + tokenAdminRegistry, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*token_admin_registry.TokenAdminRegistry] { + tokenAdminRegistryAddr, tx2, tokenAdminRegistry, err2 := token_admin_registry.DeployTokenAdminRegistry( + chain.DeployerKey, + chain.Client) + return deployment.ContractDeploy[*token_admin_registry.TokenAdminRegistry]{ + Address: tokenAdminRegistryAddr, Contract: tokenAdminRegistry, Tx: tx2, + Tv: deployment.NewTypeAndVersion(changeset.TokenAdminRegistry, deployment.Version1_5_0), Err: err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy token admin registry", "chain", chain.String(), "err", err) + return err + } + tokenAdminReg = tokenAdminRegistry.Contract + } else { + lggr.Infow("TokenAdminRegistry already deployed", "chain", chain.String(), "address", chainState.TokenAdminRegistry.Address) + tokenAdminReg = chainState.TokenAdminRegistry + } + if chainState.RegistryModule == nil { + customRegistryModule, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*registry_module_owner_custom.RegistryModuleOwnerCustom] { + regModAddr, tx2, regMod, err2 := registry_module_owner_custom.DeployRegistryModuleOwnerCustom( + chain.DeployerKey, + chain.Client, + tokenAdminReg.Address()) + return deployment.ContractDeploy[*registry_module_owner_custom.RegistryModuleOwnerCustom]{ + Address: regModAddr, Contract: regMod, Tx: tx2, + Tv: deployment.NewTypeAndVersion(changeset.RegistryModule, deployment.Version1_5_0), Err: err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy custom registry module", "chain", chain.String(), "err", err) + return err + } + registryModule = customRegistryModule.Contract + } else { + lggr.Infow("custom registry module already deployed", "chain", chain.String(), "addr", registryModule.Address) + } + isRegistryAdded, err := tokenAdminReg.IsRegistryModule(nil, registryModule.Address()) + if err != nil { + lggr.Errorw("Failed to check if registry module is added on token admin registry", "chain", chain.String(), "err", err) + return fmt.Errorf("failed to check if registry module is added on token admin registry: %w", err) + } + if !isRegistryAdded { + tx, err := tokenAdminReg.AddRegistryModule(chain.DeployerKey, registryModule.Address()) + if err != nil { + lggr.Errorw("Failed to assign registry module on token admin registry", "chain", chain.String(), "err", err) + return fmt.Errorf("failed to assign registry module on token admin registry: %w", err) + } + + _, err = chain.Confirm(tx) + if err != nil { + lggr.Errorw("Failed to confirm assign registry module on token admin registry", "chain", chain.String(), "err", err) + return fmt.Errorf("failed to confirm assign registry module on token admin registry: %w", err) + } + lggr.Infow("assigned registry module on token admin registry") + } + // ================================================================ + // │ Deploy Tokens │ + // ================================================================ + var weth9Address common.Address + var linkAddress common.Address + if chainState.Weth9 == nil { + weth, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*weth9.WETH9] { + weth9Addr, tx2, weth9c, err2 := weth9.DeployWETH9( + chain.DeployerKey, + chain.Client, + ) + return deployment.ContractDeploy[*weth9.WETH9]{ + Address: weth9Addr, Contract: weth9c, Tx: tx2, + Tv: deployment.NewTypeAndVersion(changeset.WETH9, deployment.Version1_0_0), Err: err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy weth9", "chain", chain.String(), "err", err) + return err + } + weth9Address = weth.Address + } else { + lggr.Infow("weth9 already deployed", "chain", chain.String(), "addr", chainState.Weth9.Address()) + } + // check if link token is already deployed + if chainState.LinkToken == nil { + lggr.Errorw("Link token not deployed", "chain", chain.String()) + return fmt.Errorf("link token not deployed for chain %s, deploy link first with DeployLinkToken changeset", chain.String()) + } else { + linkAddress = chainState.LinkToken.Address() + } + + // ================================================================ + // │ Deploy Routers │ + // ================================================================ + if chainState.Router == nil { + _, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*router.Router] { + routerAddr, tx2, routerC, err2 := router.DeployRouter( + chain.DeployerKey, + chain.Client, + weth9Address, + rmnProxyAddress, + ) + return deployment.ContractDeploy[*router.Router]{ + Address: routerAddr, Contract: routerC, Tx: tx2, + Tv: deployment.NewTypeAndVersion(changeset.Router, deployment.Version1_2_0), Err: err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy router", "chain", chain.String(), "err", err) + return err + } + } else { + lggr.Infow("router already deployed", "chain", chain.String(), "addr", chainState.Router.Address) + } + // ================================================================ + // │ Deploy Price Registry │ + // ================================================================ + if chainState.PriceRegistry == nil { + _, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*price_registry_1_2_0.PriceRegistry] { + priceRegAddr, tx2, priceRegAddrC, err2 := price_registry_1_2_0.DeployPriceRegistry( + chain.DeployerKey, + chain.Client, + nil, + []common.Address{weth9Address, linkAddress}, + cfg.PriceRegStalenessThreshold, + ) + return deployment.ContractDeploy[*price_registry_1_2_0.PriceRegistry]{ + Address: priceRegAddr, Contract: priceRegAddrC, Tx: tx2, + Tv: deployment.NewTypeAndVersion(changeset.PriceRegistry, deployment.Version1_2_0), Err: err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy PriceRegistry", "chain", chain.String(), "err", err) + return err + } + } else { + lggr.Infow("PriceRegistry already deployed", "chain", chain.String(), "addr", chainState.PriceRegistry.Address) + } + // ================================================================ + // │ Deploy Receiver │ + // ================================================================ + if chainState.Receiver == nil { + _, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver] { + receiverAddr, tx, receiver, err2 := maybe_revert_message_receiver.DeployMaybeRevertMessageReceiver( + chain.DeployerKey, + chain.Client, + false, + ) + return deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver]{ + Address: receiverAddr, Contract: receiver, Tx: tx, + Tv: deployment.NewTypeAndVersion(changeset.CCIPReceiver, deployment.Version1_0_0), Err: err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy receiver", "chain", chain.String(), "err", err) + return err + } + } else { + lggr.Infow("Receiver already deployed", "chain", chain.String(), "addr", chainState.Receiver.Address) } return nil } From c4ab875e1deba3b7fc85777949562bcc7d2e8e6e Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 11 Dec 2024 13:16:42 -0800 Subject: [PATCH 08/29] change --- deployment/ccip/changeset/v1_5/e2e_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 deployment/ccip/changeset/v1_5/e2e_test.go diff --git a/deployment/ccip/changeset/v1_5/e2e_test.go b/deployment/ccip/changeset/v1_5/e2e_test.go new file mode 100644 index 00000000000..1e2cfe5fc4a --- /dev/null +++ b/deployment/ccip/changeset/v1_5/e2e_test.go @@ -0,0 +1,11 @@ +package v1_5 + +import ( + "testing" + + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" +) + +func TestE2ELegacy(t *testing.T) { + e := changeset.NewMemoryEnvironment() +} From aabba802e66de21f7e2eb41fbf474920fa70dafe Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 11 Dec 2024 16:13:56 -0800 Subject: [PATCH 09/29] comments --- .../ccip/changeset/cs_add_chain_test.go | 27 +- deployment/ccip/changeset/cs_deploy_chain.go | 4 + .../ccip/changeset/cs_deploy_chain_test.go | 8 +- deployment/ccip/changeset/cs_prerequisites.go | 158 +++++--- deployment/ccip/changeset/state.go | 8 +- deployment/ccip/changeset/test_environment.go | 42 +-- .../ccip/changeset/v1_5/cs_add_lanes.go | 102 +++++ .../ccip/changeset/v1_5/cs_deploy_chains.go | 352 ------------------ deployment/ccip/changeset/v1_5/e2e_test.go | 13 +- deployment/ccip/changeset/v1_5/state.go | 20 - 10 files changed, 290 insertions(+), 444 deletions(-) delete mode 100644 deployment/ccip/changeset/v1_5/cs_deploy_chains.go delete mode 100644 deployment/ccip/changeset/v1_5/state.go diff --git a/deployment/ccip/changeset/cs_add_chain_test.go b/deployment/ccip/changeset/cs_add_chain_test.go index b21d7411ce7..8f9eef78a05 100644 --- a/deployment/ccip/changeset/cs_add_chain_test.go +++ b/deployment/ccip/changeset/cs_add_chain_test.go @@ -42,9 +42,6 @@ func TestAddChainInbound(t *testing.T) { // We deploy to the rest. initialDeploy := e.Env.AllChainSelectorsExcluding([]uint64{newChain}) newAddresses := deployment.NewMemoryAddressBook() - err = deployPrerequisiteChainContracts(e.Env, newAddresses, initialDeploy, nil) - require.NoError(t, err) - require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) cfg := commontypes.MCMSWithTimelockConfig{ Canceller: commonchangeset.SingleGroupMCMS(t), @@ -57,6 +54,22 @@ func TestAddChainInbound(t *testing.T) { Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployLinkToken), Config: initialDeploy, }, + { + Changeset: commonchangeset.WrapChangeSet(DeployPrerequisites), + Config: DeployPrerequisiteConfig{ + Configs: []DeployPrerequisiteConfigPerChain{ + { + ChainSelector: initialDeploy[0], + }, + { + ChainSelector: initialDeploy[1], + }, + { + ChainSelector: initialDeploy[2], + }, + }, + }, + }, { Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployMCMSWithTimelock), Config: map[uint64]commontypes.MCMSWithTimelockConfig{ @@ -128,7 +141,13 @@ func TestAddChainInbound(t *testing.T) { require.NoError(t, err) newAddresses = deployment.NewMemoryAddressBook() - err = deployPrerequisiteChainContracts(e.Env, newAddresses, []uint64{newChain}, nil) + err = deployPrerequisiteChainContracts(e.Env, newAddresses, DeployPrerequisiteConfig{ + Configs: []DeployPrerequisiteConfigPerChain{ + { + ChainSelector: newChain, + }, + }, + }) require.NoError(t, err) require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses)) newAddresses = deployment.NewMemoryAddressBook() diff --git a/deployment/ccip/changeset/cs_deploy_chain.go b/deployment/ccip/changeset/cs_deploy_chain.go index b6a738fd198..5a8d8cf7843 100644 --- a/deployment/ccip/changeset/cs_deploy_chain.go +++ b/deployment/ccip/changeset/cs_deploy_chain.go @@ -30,6 +30,10 @@ var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContracts // DeployChainContracts is idempotent. 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. +// Points to note : +// In case of migrating from legacy ccip to 1.6, the previous RMN address should be set while deploying RMNRemote. +// if there is no existing RMN address found, RMNRemote will be deployed with 0x0 address for previous RMN address +// which will set RMN to 0x0 address immutably in RMNRemote. func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig) (deployment.ChangesetOutput, error) { if err := c.Validate(); err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig: %w", err) diff --git a/deployment/ccip/changeset/cs_deploy_chain_test.go b/deployment/ccip/changeset/cs_deploy_chain_test.go index fbf9c881138..9c977241ca1 100644 --- a/deployment/ccip/changeset/cs_deploy_chain_test.go +++ b/deployment/ccip/changeset/cs_deploy_chain_test.go @@ -37,6 +37,12 @@ func TestDeployChainContractsChangeset(t *testing.T) { TimelockMinDelay: big.NewInt(0), } } + var prereqCfg []DeployPrerequisiteConfigPerChain + for _, chain := range e.AllChainSelectors() { + prereqCfg = append(prereqCfg, DeployPrerequisiteConfigPerChain{ + ChainSelector: chain, + }) + } e, err = commonchangeset.ApplyChangesets(t, e, nil, []commonchangeset.ChangesetApplication{ { Changeset: commonchangeset.WrapChangeSet(DeployHomeChain), @@ -61,7 +67,7 @@ func TestDeployChainContractsChangeset(t *testing.T) { { Changeset: commonchangeset.WrapChangeSet(DeployPrerequisites), Config: DeployPrerequisiteConfig{ - ChainSelectors: selectors, + Configs: prereqCfg, }, }, { diff --git a/deployment/ccip/changeset/cs_prerequisites.go b/deployment/ccip/changeset/cs_prerequisites.go index 883fa1b1db6..d611a9aae6b 100644 --- a/deployment/ccip/changeset/cs_prerequisites.go +++ b/deployment/ccip/changeset/cs_prerequisites.go @@ -10,7 +10,9 @@ import ( "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_proxy_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/token_admin_registry" @@ -32,7 +34,7 @@ func DeployPrerequisites(env deployment.Environment, cfg DeployPrerequisiteConfi return deployment.ChangesetOutput{}, errors.Wrapf(deployment.ErrInvalidConfig, "%v", err) } ab := deployment.NewMemoryAddressBook() - err = deployPrerequisiteChainContracts(env, ab, cfg.ChainSelectors, cfg.Opts...) + err = deployPrerequisiteChainContracts(env, ab, cfg) if err != nil { env.Logger.Errorw("Failed to deploy prerequisite contracts", "err", err, "addressBook", ab) return deployment.ChangesetOutput{ @@ -47,13 +49,23 @@ func DeployPrerequisites(env deployment.Environment, cfg DeployPrerequisiteConfi } type DeployPrerequisiteContractsOpts struct { - USDCEnabledChains []uint64 - Multicall3Enabled bool + USDCEnabled bool + Multicall3Enabled bool + LegacyDeploymentCfg *LegacyDeploymentConfig +} + +type LegacyDeploymentConfig struct { + RMNConfig *rmn_contract.RMNConfig + PriceRegStalenessThreshold uint32 } type DeployPrerequisiteConfig struct { - ChainSelectors []uint64 - Opts []PrerequisiteOpt + Configs []DeployPrerequisiteConfigPerChain +} + +type DeployPrerequisiteConfigPerChain struct { + ChainSelector uint64 + Opts []PrerequisiteOpt // TODO handle tokens and feeds in prerequisite config Tokens map[TokenSymbol]common.Address Feeds map[TokenSymbol]common.Address @@ -61,7 +73,8 @@ type DeployPrerequisiteConfig struct { func (c DeployPrerequisiteConfig) Validate() error { mapAllChainSelectors := make(map[uint64]struct{}) - for _, cs := range c.ChainSelectors { + for _, cfg := range c.Configs { + cs := cfg.ChainSelector mapAllChainSelectors[cs] = struct{}{} if err := deployment.IsValidChainSelector(cs); err != nil { return fmt.Errorf("invalid chain selector: %d - %w", cs, err) @@ -72,31 +85,41 @@ func (c DeployPrerequisiteConfig) Validate() error { type PrerequisiteOpt func(o *DeployPrerequisiteContractsOpts) -func WithUSDCChains(chains []uint64) PrerequisiteOpt { +func WithUSDCEnabled() PrerequisiteOpt { + return func(o *DeployPrerequisiteContractsOpts) { + o.USDCEnabled = true + } +} + +func WithMulticall3() PrerequisiteOpt { return func(o *DeployPrerequisiteContractsOpts) { - o.USDCEnabledChains = chains + o.Multicall3Enabled = true } } -func WithMulticall3(enabled bool) PrerequisiteOpt { +func WithLegacyDeployment(cfg LegacyDeploymentConfig) PrerequisiteOpt { return func(o *DeployPrerequisiteContractsOpts) { - o.Multicall3Enabled = enabled + if cfg.PriceRegStalenessThreshold == 0 { + panic("PriceRegStalenessThreshold must be set") + } + // TODO validate RMNConfig + o.LegacyDeploymentCfg = &cfg } } -func deployPrerequisiteChainContracts(e deployment.Environment, ab deployment.AddressBook, selectors []uint64, opts ...PrerequisiteOpt) error { +func deployPrerequisiteChainContracts(e deployment.Environment, ab deployment.AddressBook, cfg DeployPrerequisiteConfig) error { state, err := LoadOnchainState(e) if err != nil { e.Logger.Errorw("Failed to load existing onchain state", "err") return err } deployGrp := errgroup.Group{} - for _, sel := range selectors { - chain := e.Chains[sel] + for _, c := range cfg.Configs { + chain := e.Chains[c.ChainSelector] deployGrp.Go(func() error { - err := deployPrerequisiteContracts(e, ab, state, chain, opts...) + err := deployPrerequisiteContracts(e, ab, state, chain, c.Opts...) if err != nil { - e.Logger.Errorw("Failed to deploy prerequisite contracts", "chain", sel, "err", err) + e.Logger.Errorw("Failed to deploy prerequisite contracts", "chain", chain.String(), "err", err) return err } return nil @@ -114,13 +137,6 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address opt(deployOpts) } } - var isUSDC bool - for _, sel := range deployOpts.USDCEnabledChains { - if sel == chain.Selector { - isUSDC = true - break - } - } lggr := e.Logger chainState, chainExists := state.Chains[chain.Selector] var weth9Contract *weth9.WETH9 @@ -137,31 +153,60 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address r = chainState.Router mc3 = chainState.Multicall3 } - if rmnProxy == nil { - // we want to replicate the mainnet scenario where RMNProxy is already deployed with some existing RMN - // This will need us to use two different RMNProxy contracts - // 1. RMNProxyNew with RMNRemote - ( deployed later in chain contracts) - // 2. RMNProxy with mockRMN - ( deployed here, replicating the behavior of existing RMNProxy with already set RMN) - rmn, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract] { - rmnAddr, tx2, rmn, err2 := mock_rmn_contract.DeployMockRMNContract( - chain.DeployerKey, - chain.Client, - ) - return deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract]{ - rmnAddr, rmn, tx2, deployment.NewTypeAndVersion(MockRMN, deployment.Version1_0_0), err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy mock RMN", "chain", chain.String(), "err", err) - return err + var rmnAddr common.Address + // if we are setting up 1.5 version, deploy RMN contract based on the config provided + // else deploy the mock RMN contract + if deployOpts.LegacyDeploymentCfg != nil && deployOpts.LegacyDeploymentCfg.RMNConfig != nil { + if chainState.RMN == nil { + rmn, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*rmn_contract.RMNContract] { + rmnAddress, tx2, rmnC, err2 := rmn_contract.DeployRMNContract( + chain.DeployerKey, + chain.Client, + *deployOpts.LegacyDeploymentCfg.RMNConfig, + ) + return deployment.ContractDeploy[*rmn_contract.RMNContract]{ + Address: rmnAddress, Contract: rmnC, Tx: tx2, Tv: deployment.NewTypeAndVersion(RMN, deployment.Version1_5_0), Err: err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy RMN", "chain", chain.String(), "err", err) + return err + } + rmnAddr = rmn.Address + } else { + lggr.Infow("RMN already deployed", "chain", chain.String(), "address", chainState.RMN.Address) + rmnAddr = chainState.RMN.Address() } + } else { + if chainState.MockRMN == nil { + rmn, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract] { + rmnAddress, tx2, rmnC, err2 := mock_rmn_contract.DeployMockRMNContract( + chain.DeployerKey, + chain.Client, + ) + return deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract]{ + rmnAddress, rmnC, tx2, deployment.NewTypeAndVersion(MockRMN, deployment.Version1_0_0), err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy mock RMN", "chain", chain.String(), "err", err) + return err + } + rmnAddr = rmn.Address + } else { + lggr.Infow("Mock RMN already deployed", "chain", chain.String(), "addr", chainState.MockRMN.Address) + rmnAddr = chainState.MockRMN.Address() + } + } + if rmnProxy == nil { rmnProxyContract, err := deployment.DeployContract(lggr, chain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract] { rmnProxyAddr, tx2, rmnProxy, err2 := rmn_proxy_contract.DeployRMNProxyContract( chain.DeployerKey, chain.Client, - rmn.Address, + rmnAddr, ) return deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract]{ rmnProxyAddr, rmnProxy, tx2, deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_0_0), err2, @@ -172,6 +217,8 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address return err } rmnProxy = rmnProxyContract.Contract + } else { + lggr.Infow("RMNProxy already deployed", "chain", chain.String(), "addr", rmnProxy.Address) } if tokenAdminReg == nil { tokenAdminRegistry, err := deployment.DeployContract(e.Logger, chain, ab, @@ -247,7 +294,9 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address weth9Contract = weth.Contract } else { lggr.Infow("weth9 already deployed", "chain", chain.String(), "addr", weth9Contract.Address) + weth9Contract = chainState.Weth9 } + // if router is not already deployed, we deploy it if r == nil { routerContract, err := deployment.DeployContract(e.Logger, chain, ab, @@ -291,7 +340,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address e.Logger.Info("ccip multicall already deployed", "chain", chain.String(), "addr", mc3.Address) } } - if isUSDC { + if deployOpts.USDCEnabled { token, pool, messenger, transmitter, err1 := DeployUSDC(e.Logger, chain, ab, rmnProxy.Address(), r.Address()) if err1 != nil { return err1 @@ -304,5 +353,30 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address "messenger", messenger.Address(), ) } + // Only applicable if setting up for 1.5 version, remove this once we have fully migrated to 1.6 + if deployOpts.LegacyDeploymentCfg != nil { + if chainState.PriceRegistry == nil { + _, err := deployment.DeployContract(lggr, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*price_registry_1_2_0.PriceRegistry] { + priceRegAddr, tx2, priceRegAddrC, err2 := price_registry_1_2_0.DeployPriceRegistry( + chain.DeployerKey, + chain.Client, + nil, + []common.Address{weth9Contract.Address(), chainState.LinkToken.Address()}, + deployOpts.LegacyDeploymentCfg.PriceRegStalenessThreshold, + ) + return deployment.ContractDeploy[*price_registry_1_2_0.PriceRegistry]{ + Address: priceRegAddr, Contract: priceRegAddrC, Tx: tx2, + Tv: deployment.NewTypeAndVersion(PriceRegistry, deployment.Version1_2_0), Err: err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy PriceRegistry", "chain", chain.String(), "err", err) + return err + } + } else { + lggr.Infow("PriceRegistry already deployed", "chain", chain.String(), "addr", chainState.PriceRegistry.Address) + } + } return nil } diff --git a/deployment/ccip/changeset/state.go b/deployment/ccip/changeset/state.go index 86bafce9fff..da24b7ac3cd 100644 --- a/deployment/ccip/changeset/state.go +++ b/deployment/ccip/changeset/state.go @@ -20,7 +20,6 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/deployment" - legacychangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_5" "github.com/smartcontractkit/chainlink/deployment/ccip/view" "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_0" "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_2" @@ -129,7 +128,12 @@ type CCIPChainState struct { Multicall3 *multicall3.Multicall3 // Legacy contracts - legacychangeset.CCIPChainStateLegacy + EVM2EVMOnRamp map[uint64]*evm_2_evm_onramp.EVM2EVMOnRamp // mapping of dest chain selector -> EVM2EVMOnRamp + CommitStore map[uint64]*commit_store.CommitStore // mapping of source chain selector -> CommitStore + EVM2EVMOffRamp map[uint64]*evm_2_evm_offramp.EVM2EVMOffRamp // mapping of source chain selector -> EVM2EVMOffRamp + MockRMN *mock_rmn_contract.MockRMNContract + PriceRegistry *price_registry_1_2_0.PriceRegistry + RMN *rmn_contract.RMNContract } func (c CCIPChainState) GenerateView() (view.ChainView, error) { diff --git a/deployment/ccip/changeset/test_environment.go b/deployment/ccip/changeset/test_environment.go index ede078254c2..171190c7308 100644 --- a/deployment/ccip/changeset/test_environment.go +++ b/deployment/ccip/changeset/test_environment.go @@ -306,15 +306,22 @@ func NewEnvironmentWithJobsAndContracts(t *testing.T, tc *TestConfigs, tEnv Test TimelockMinDelay: big.NewInt(0), } } - var ( - usdcChains []uint64 - isMulticall3 bool - ) - if tc != nil { - if tc.IsUSDC { - usdcChains = allChains + + var prereqCfg []DeployPrerequisiteConfigPerChain + for _, chain := range allChains { + var opts []PrerequisiteOpt + if tc != nil { + if tc.IsUSDC { + opts = append(opts, WithUSDCEnabled()) + } + if tc.IsMultiCall3 { + opts = append(opts, WithMulticall3()) + } } - isMulticall3 = tc.IsMultiCall3 + prereqCfg = append(prereqCfg, DeployPrerequisiteConfigPerChain{ + ChainSelector: chain, + Opts: opts, + }) } // Need to deploy prerequisites first so that we can form the USDC config // no proposals to be made, timelock can be passed as nil here @@ -326,11 +333,7 @@ func NewEnvironmentWithJobsAndContracts(t *testing.T, tc *TestConfigs, tEnv Test { Changeset: commonchangeset.WrapChangeSet(DeployPrerequisites), Config: DeployPrerequisiteConfig{ - ChainSelectors: allChains, - Opts: []PrerequisiteOpt{ - WithUSDCChains(usdcChains), - WithMulticall3(isMulticall3), - }, + Configs: prereqCfg, }, }, { @@ -349,22 +352,19 @@ func NewEnvironmentWithJobsAndContracts(t *testing.T, tc *TestConfigs, tEnv Test state, err := LoadOnchainState(e.Env) require.NoError(t, err) - // Assert USDC set up as expected. - for _, chain := range usdcChains { - require.NotNil(t, state.Chains[chain].MockUSDCTokenMessenger) - require.NotNil(t, state.Chains[chain].MockUSDCTransmitter) - require.NotNil(t, state.Chains[chain].USDCTokenPool) - } // Assert link present require.NotNil(t, state.Chains[e.FeedChainSel].LinkToken) require.NotNil(t, state.Chains[e.FeedChainSel].Weth9) tokenConfig := NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) var tokenDataProviders []pluginconfig.TokenDataObserverConfig - if len(usdcChains) > 0 { + if tc.IsUSDC { endpoint := tEnv.MockUSDCAttestationServer(t, tc.IsUSDCAttestationMissing) cctpContracts := make(map[cciptypes.ChainSelector]pluginconfig.USDCCCTPTokenConfig) - for _, usdcChain := range usdcChains { + for _, usdcChain := range allChains { + require.NotNil(t, state.Chains[usdcChain].MockUSDCTokenMessenger) + require.NotNil(t, state.Chains[usdcChain].MockUSDCTransmitter) + require.NotNil(t, state.Chains[usdcChain].USDCTokenPool) cctpContracts[cciptypes.ChainSelector(usdcChain)] = pluginconfig.USDCCCTPTokenConfig{ SourcePoolAddress: state.Chains[usdcChain].USDCTokenPool.Address().String(), SourceMessageTransmitterAddr: state.Chains[usdcChain].MockUSDCTransmitter.Address().String(), diff --git a/deployment/ccip/changeset/v1_5/cs_add_lanes.go b/deployment/ccip/changeset/v1_5/cs_add_lanes.go index 78a68cafc6e..46e79a589af 100644 --- a/deployment/ccip/changeset/v1_5/cs_add_lanes.go +++ b/deployment/ccip/changeset/v1_5/cs_add_lanes.go @@ -1,9 +1,12 @@ package v1_5 import ( + "context" "fmt" + "strconv" "github.com/ethereum/go-ethereum/common" + chain_selectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" @@ -12,6 +15,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" + integrationtesthelpers "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers/integration" ) type AddLanesConfig struct { @@ -45,6 +50,12 @@ type AddLaneConfig struct { // Price Registry specific configuration InitialTokenPrices []price_registry_1_2_0.InternalTokenPriceUpdate GasPriceUpdates []price_registry_1_2_0.InternalGasPriceUpdate + + // Job specific configuration + TokenPricesUSDPipeline string + PriceGetterConfigJson string + USDCAttestationAPI string + USDCCfg *config.USDCConfig } func (c *AddLaneConfig) Validate(e deployment.Environment, state changeset.CCIPOnChainState) error { @@ -90,18 +101,109 @@ func AddLanes(env deployment.Environment, c AddLanesConfig) (deployment.Changese return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig: %w", err) } newAddresses := deployment.NewMemoryAddressBook() + blocksByDest := make(map[uint64]uint64) for _, cfg := range c.Configs { + number, err := env.Chains[cfg.DestinationChainSelector].Client.HeaderByNumber(context.Background(), nil) + if err != nil { + return deployment.ChangesetOutput{ + AddressBook: newAddresses, + }, err + } + blocksByDest[cfg.DestinationChainSelector] = number.Number.Uint64() if err := addLane(env, state, newAddresses, cfg); err != nil { return deployment.ChangesetOutput{ AddressBook: newAddresses, }, err } } + jobSpecs, err := jobSpecsForLane(env, state, c, blocksByDest) + if err != nil { + return deployment.ChangesetOutput{ + AddressBook: newAddresses, + }, err + } return deployment.ChangesetOutput{ AddressBook: newAddresses, + JobSpecs: jobSpecs, }, nil } +func jobSpecsForLane( + env deployment.Environment, + state changeset.CCIPOnChainState, + addLanesCfg AddLanesConfig, + blocksByDest map[uint64]uint64, +) (map[string][]string, error) { + nodes, err := deployment.NodeInfo(env.NodeIDs, env.Offchain) + if err != nil { + return nil, err + } + nodesToJobSpecs := make(map[string][]string) + for _, node := range nodes { + var specs []string + for _, cfg := range addLanesCfg.Configs { + var err error + destChainState := state.Chains[cfg.DestinationChainSelector] + sourceChain := env.Chains[cfg.SourceChainSelector] + destChain := env.Chains[cfg.DestinationChainSelector] + destEVMChainIdStr, err := chain_selectors.GetChainIDFromSelector(cfg.DestinationChainSelector) + if err != nil { + return nil, fmt.Errorf("failed to get chain ID from selector for chain %s: %w", destChain.String(), err) + } + destEVMChainId, err := strconv.ParseUint(destEVMChainIdStr, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse chain ID %s for chain %s: %w", destEVMChainIdStr, destChain.String(), err) + } + ccipJobParam := integrationtesthelpers.CCIPJobSpecParams{ + OffRamp: destChainState.EVM2EVMOffRamp[cfg.SourceChainSelector].Address(), + CommitStore: destChainState.CommitStore[cfg.SourceChainSelector].Address(), + SourceChainName: sourceChain.Name(), + DestChainName: destChain.Name(), + DestEvmChainId: destEVMChainId, + TokenPricesUSDPipeline: cfg.TokenPricesUSDPipeline, + PriceGetterConfig: cfg.PriceGetterConfigJson, + DestStartBlock: blocksByDest[cfg.DestinationChainSelector], + USDCAttestationAPI: cfg.USDCAttestationAPI, + USDCConfig: cfg.USDCCfg, + P2PV2Bootstrappers: nodes.BootstrapLocators(), + } + if !node.IsBootstrap { + commitSpec, err := ccipJobParam.CommitJobSpec() + if err != nil { + return nil, fmt.Errorf("failed to generate commit job spec for source %s and destination %s: %w", + sourceChain.String(), destChain.String(), err) + } + commitSpecStr, err := commitSpec.String() + if err != nil { + return nil, fmt.Errorf("failed to convert commit job spec to string for source %s and destination %s: %w", + sourceChain.String(), destChain.String(), err) + } + execSpec, err := ccipJobParam.ExecutionJobSpec() + if err != nil { + return nil, fmt.Errorf("failed to generate execution job spec for source %s and destination %s: %w", + sourceChain.String(), destChain.String(), err) + } + execSpecStr, err := execSpec.String() + if err != nil { + return nil, fmt.Errorf("failed to convert execution job spec to string for source %s and destination %s: %w", + sourceChain.String(), destChain.String(), err) + } + specs = append(specs, commitSpecStr, execSpecStr) + } else { + bootstrapSpec := ccipJobParam.BootstrapJob(destChainState.CommitStore[cfg.SourceChainSelector].Address().String()) + bootstrapSpecStr, err := bootstrapSpec.String() + if err != nil { + return nil, fmt.Errorf("failed to convert bootstrap job spec to string for source %s and destination %s: %w", + sourceChain.String(), destChain.String(), err) + } + specs = append(specs, bootstrapSpecStr) + } + } + nodesToJobSpecs[node.NodeID] = append(nodesToJobSpecs[node.NodeID], specs...) + } + return nodesToJobSpecs, nil +} + func addLane(e deployment.Environment, state changeset.CCIPOnChainState, ab deployment.AddressBook, cfg AddLaneConfig) error { // update prices on the source price registry sourceChainState := state.Chains[cfg.SourceChainSelector] diff --git a/deployment/ccip/changeset/v1_5/cs_deploy_chains.go b/deployment/ccip/changeset/v1_5/cs_deploy_chains.go deleted file mode 100644 index ebd33d423f4..00000000000 --- a/deployment/ccip/changeset/v1_5/cs_deploy_chains.go +++ /dev/null @@ -1,352 +0,0 @@ -package v1_5 - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" - - "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_proxy_contract" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/token_admin_registry" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/weth9" -) - -var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContracts - -type DeployChainContractsConfig struct { - configs []DeployChainContractsConfigPerChain -} - -type DeployChainContractsConfigPerChain struct { - ChainSelector uint64 - RMNConfig *rmn_contract.RMNConfig - PriceRegStalenessThreshold uint32 -} - -func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig) (deployment.ChangesetOutput, error) { - if err := c.Validate(); err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig: %w", err) - } - newAddresses := deployment.NewMemoryAddressBook() - err := deployChainContractsForChains(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) - } - return deployment.ChangesetOutput{ - Proposals: []timelock.MCMSWithTimelockProposal{}, - AddressBook: newAddresses, - JobSpecs: nil, - }, nil -} - -func (c DeployChainContractsConfig) Validate() error { - for _, cs := range c.configs { - if err := deployment.IsValidChainSelector(cs.ChainSelector); err != nil { - return fmt.Errorf("invalid chain selector: %d - %w", cs, err) - } - } - // TODO: Add rest of the config validation - return nil -} - -func deployChainContractsForChains(env deployment.Environment, ab deployment.AddressBook, cfg DeployChainContractsConfig) error { - state, err := changeset.LoadOnchainState(env) - if err != nil { - return fmt.Errorf("failed to load onchain state: %w", err) - } - for _, c := range cfg.configs { - if err := deployChainContracts(env, ab, state, c); err != nil { - return fmt.Errorf("failed to deploy CCIP contracts for chain %d: %w", c.ChainSelector, err) - } - } - return nil -} - -func deployChainContracts( - env deployment.Environment, - ab deployment.AddressBook, - state changeset.CCIPOnChainState, - cfg DeployChainContractsConfigPerChain) error { - chain, ok := env.Chains[cfg.ChainSelector] - if !ok { - return fmt.Errorf("chain %d not found", cfg.ChainSelector) - } - chainState, ok := state.Chains[cfg.ChainSelector] - if !ok { - return fmt.Errorf("chain %d state not found", cfg.ChainSelector) - } - lggr := env.Logger - // ================================================================ - // │ Deploy RMN │ - // ================================================================ - var rmnAddr common.Address - if cfg.RMNConfig == nil { - if chainState.MockRMN == nil { - rmn, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract] { - rmnAddr, tx2, rmn, err2 := mock_rmn_contract.DeployMockRMNContract( - chain.DeployerKey, - chain.Client, - ) - return deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract]{ - Address: rmnAddr, Contract: rmn, Tx: tx2, Tv: deployment.NewTypeAndVersion(changeset.MockRMN, deployment.Version1_0_0), Err: err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy mock RMN", "chain", chain.String(), "err", err) - return err - } - rmnAddr = rmn.Address - } else { - lggr.Infow("MockRMN already deployed", "chain", chain.String(), "address", chainState.MockRMN.Address()) - rmnAddr = chainState.MockRMN.Address() - } - } else { - if chainState.RMN == nil { - rmn, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*rmn_contract.RMNContract] { - rmnAddr, tx2, rmn, err2 := rmn_contract.DeployRMNContract( - chain.DeployerKey, - chain.Client, - *cfg.RMNConfig, - ) - return deployment.ContractDeploy[*rmn_contract.RMNContract]{ - Address: rmnAddr, Contract: rmn, Tx: tx2, Tv: deployment.NewTypeAndVersion(changeset.RMN, deployment.Version1_5_0), Err: err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy RMN", "chain", chain.String(), "err", err) - return err - } - rmnAddr = rmn.Address - } else { - lggr.Infow("RMN already deployed", "chain", chain.String(), "address", chainState.RMN.Address) - rmnAddr = chainState.RMN.Address() - } - } - var rmnProxyAddress common.Address - if chainState.RMNProxy == nil { - rmnProxy, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract] { - rmnProxyAddr, tx2, rmnProxy, err2 := rmn_proxy_contract.DeployRMNProxyContract( - chain.DeployerKey, - chain.Client, - rmnAddr, - ) - return deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract]{ - Address: rmnProxyAddr, Contract: rmnProxy, Tx: tx2, Tv: deployment.NewTypeAndVersion(changeset.ARMProxy, deployment.Version1_0_0), Err: err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy RMNProxy", "chain", chain.String(), "err", err) - return err - } - rmnProxyAddress = rmnProxy.Address - } else { - lggr.Infow("RMNProxy already deployed", "chain", chain.String(), "address", chainState.RMNProxy.Address) - // check if the RMNProxy is pointing to the correct RMN - rmnProxy := chainState.RMNProxy - setRMN, err := rmnProxy.GetARM(nil) - if err != nil { - return err - } - if setRMN != rmnAddr { - lggr.Infow("RMNProxy pointing to wrong RMN, changing the RMN", "chain", chain.String(), "rmnProxy", rmnProxy.Address, "rmn", rmnAddr) - tx, err := rmnProxy.SetARM(chain.DeployerKey, rmnAddr) - if err != nil { - return fmt.Errorf("failed to set RMN on RMNProxy for chain %s RMN %s RMNProxy %s: %w", - chain.String(), rmnAddr.String(), rmnProxy.Address().String(), err) - } - - _, err = chain.Confirm(tx) - if err != nil { - lggr.Errorw("Failed to confirm RMNProxy SetARM", - "chain", chain.String(), "rmn", rmnAddr.String(), "rmnProxy", rmnProxy.Address().String(), "err", err) - return fmt.Errorf("failed to confirm RMNProxy SetARM for chain %s RMN %s RMNProxy %s: %w", - chain.String(), rmnAddr.String(), rmnProxy.Address().String(), err) - } - lggr.Infow("RMNProxy SetARM confirmed", - "chain", chain.String(), "rmn", rmnAddr.String(), "rmnProxy", rmnProxy.Address().String()) - } - rmnProxyAddress = rmnProxy.Address() - } - // ================================================================ - // │ Deploy TokenAdminRegistry │ - // ================================================================ - var tokenAdminReg *token_admin_registry.TokenAdminRegistry - var registryModule *registry_module_owner_custom.RegistryModuleOwnerCustom - if chainState.TokenAdminRegistry == nil { - tokenAdminRegistry, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*token_admin_registry.TokenAdminRegistry] { - tokenAdminRegistryAddr, tx2, tokenAdminRegistry, err2 := token_admin_registry.DeployTokenAdminRegistry( - chain.DeployerKey, - chain.Client) - return deployment.ContractDeploy[*token_admin_registry.TokenAdminRegistry]{ - Address: tokenAdminRegistryAddr, Contract: tokenAdminRegistry, Tx: tx2, - Tv: deployment.NewTypeAndVersion(changeset.TokenAdminRegistry, deployment.Version1_5_0), Err: err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy token admin registry", "chain", chain.String(), "err", err) - return err - } - tokenAdminReg = tokenAdminRegistry.Contract - } else { - lggr.Infow("TokenAdminRegistry already deployed", "chain", chain.String(), "address", chainState.TokenAdminRegistry.Address) - tokenAdminReg = chainState.TokenAdminRegistry - } - if chainState.RegistryModule == nil { - customRegistryModule, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*registry_module_owner_custom.RegistryModuleOwnerCustom] { - regModAddr, tx2, regMod, err2 := registry_module_owner_custom.DeployRegistryModuleOwnerCustom( - chain.DeployerKey, - chain.Client, - tokenAdminReg.Address()) - return deployment.ContractDeploy[*registry_module_owner_custom.RegistryModuleOwnerCustom]{ - Address: regModAddr, Contract: regMod, Tx: tx2, - Tv: deployment.NewTypeAndVersion(changeset.RegistryModule, deployment.Version1_5_0), Err: err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy custom registry module", "chain", chain.String(), "err", err) - return err - } - registryModule = customRegistryModule.Contract - } else { - lggr.Infow("custom registry module already deployed", "chain", chain.String(), "addr", registryModule.Address) - } - isRegistryAdded, err := tokenAdminReg.IsRegistryModule(nil, registryModule.Address()) - if err != nil { - lggr.Errorw("Failed to check if registry module is added on token admin registry", "chain", chain.String(), "err", err) - return fmt.Errorf("failed to check if registry module is added on token admin registry: %w", err) - } - if !isRegistryAdded { - tx, err := tokenAdminReg.AddRegistryModule(chain.DeployerKey, registryModule.Address()) - if err != nil { - lggr.Errorw("Failed to assign registry module on token admin registry", "chain", chain.String(), "err", err) - return fmt.Errorf("failed to assign registry module on token admin registry: %w", err) - } - - _, err = chain.Confirm(tx) - if err != nil { - lggr.Errorw("Failed to confirm assign registry module on token admin registry", "chain", chain.String(), "err", err) - return fmt.Errorf("failed to confirm assign registry module on token admin registry: %w", err) - } - lggr.Infow("assigned registry module on token admin registry") - } - // ================================================================ - // │ Deploy Tokens │ - // ================================================================ - var weth9Address common.Address - var linkAddress common.Address - if chainState.Weth9 == nil { - weth, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*weth9.WETH9] { - weth9Addr, tx2, weth9c, err2 := weth9.DeployWETH9( - chain.DeployerKey, - chain.Client, - ) - return deployment.ContractDeploy[*weth9.WETH9]{ - Address: weth9Addr, Contract: weth9c, Tx: tx2, - Tv: deployment.NewTypeAndVersion(changeset.WETH9, deployment.Version1_0_0), Err: err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy weth9", "chain", chain.String(), "err", err) - return err - } - weth9Address = weth.Address - } else { - lggr.Infow("weth9 already deployed", "chain", chain.String(), "addr", chainState.Weth9.Address()) - } - // check if link token is already deployed - if chainState.LinkToken == nil { - lggr.Errorw("Link token not deployed", "chain", chain.String()) - return fmt.Errorf("link token not deployed for chain %s, deploy link first with DeployLinkToken changeset", chain.String()) - } else { - linkAddress = chainState.LinkToken.Address() - } - - // ================================================================ - // │ Deploy Routers │ - // ================================================================ - if chainState.Router == nil { - _, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*router.Router] { - routerAddr, tx2, routerC, err2 := router.DeployRouter( - chain.DeployerKey, - chain.Client, - weth9Address, - rmnProxyAddress, - ) - return deployment.ContractDeploy[*router.Router]{ - Address: routerAddr, Contract: routerC, Tx: tx2, - Tv: deployment.NewTypeAndVersion(changeset.Router, deployment.Version1_2_0), Err: err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy router", "chain", chain.String(), "err", err) - return err - } - } else { - lggr.Infow("router already deployed", "chain", chain.String(), "addr", chainState.Router.Address) - } - // ================================================================ - // │ Deploy Price Registry │ - // ================================================================ - if chainState.PriceRegistry == nil { - _, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*price_registry_1_2_0.PriceRegistry] { - priceRegAddr, tx2, priceRegAddrC, err2 := price_registry_1_2_0.DeployPriceRegistry( - chain.DeployerKey, - chain.Client, - nil, - []common.Address{weth9Address, linkAddress}, - cfg.PriceRegStalenessThreshold, - ) - return deployment.ContractDeploy[*price_registry_1_2_0.PriceRegistry]{ - Address: priceRegAddr, Contract: priceRegAddrC, Tx: tx2, - Tv: deployment.NewTypeAndVersion(changeset.PriceRegistry, deployment.Version1_2_0), Err: err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy PriceRegistry", "chain", chain.String(), "err", err) - return err - } - } else { - lggr.Infow("PriceRegistry already deployed", "chain", chain.String(), "addr", chainState.PriceRegistry.Address) - } - // ================================================================ - // │ Deploy Receiver │ - // ================================================================ - if chainState.Receiver == nil { - _, err := deployment.DeployContract(lggr, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver] { - receiverAddr, tx, receiver, err2 := maybe_revert_message_receiver.DeployMaybeRevertMessageReceiver( - chain.DeployerKey, - chain.Client, - false, - ) - return deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver]{ - Address: receiverAddr, Contract: receiver, Tx: tx, - Tv: deployment.NewTypeAndVersion(changeset.CCIPReceiver, deployment.Version1_0_0), Err: err2, - } - }) - if err != nil { - lggr.Errorw("Failed to deploy receiver", "chain", chain.String(), "err", err) - return err - } - } else { - lggr.Infow("Receiver already deployed", "chain", chain.String(), "addr", chainState.Receiver.Address) - } - return nil -} diff --git a/deployment/ccip/changeset/v1_5/e2e_test.go b/deployment/ccip/changeset/v1_5/e2e_test.go index 1e2cfe5fc4a..114343fd0d2 100644 --- a/deployment/ccip/changeset/v1_5/e2e_test.go +++ b/deployment/ccip/changeset/v1_5/e2e_test.go @@ -3,9 +3,18 @@ package v1_5 import ( "testing" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "go.uber.org/zap/zapcore" + + "github.com/smartcontractkit/chainlink/deployment/environment/memory" + "github.com/smartcontractkit/chainlink/v2/core/logger" ) func TestE2ELegacy(t *testing.T) { - e := changeset.NewMemoryEnvironment() + e := memory.NewMemoryEnvironment(t, logger.TestLogger(t), zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Chains: 2, + NumOfUsersPerChain: 1, + Nodes: 4, + Bootstraps: 1, + }) + } diff --git a/deployment/ccip/changeset/v1_5/state.go b/deployment/ccip/changeset/v1_5/state.go deleted file mode 100644 index 29649a0db81..00000000000 --- a/deployment/ccip/changeset/v1_5/state.go +++ /dev/null @@ -1,20 +0,0 @@ -package v1_5 - -import ( - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" -) - -// CCIPChainStateLegacy holds 1.5 state for CCIP chains -type CCIPChainStateLegacy struct { - EVM2EVMOnRamp map[uint64]*evm_2_evm_onramp.EVM2EVMOnRamp - CommitStore map[uint64]*commit_store.CommitStore - EVM2EVMOffRamp map[uint64]*evm_2_evm_offramp.EVM2EVMOffRamp - MockRMN *mock_rmn_contract.MockRMNContract - PriceRegistry *price_registry_1_2_0.PriceRegistry - RMN *rmn_contract.RMNContract -} From 24dd0c962ef3bbc51c67c7b275dc9e5f5ed2d7bc Mon Sep 17 00:00:00 2001 From: AnieeG Date: Thu, 12 Dec 2024 16:33:11 -0800 Subject: [PATCH 10/29] progress so far --- .../ccip/changeset/v1_5/cs_add_lanes.go | 2 + .../ccip/changeset/v1_5/cs_ocr2_config.go | 218 ++++++++++++++++++ deployment/ccip/changeset/v1_5/e2e_test.go | 2 +- .../ccip/changeset/v1_5/test_helpers.go | 80 +++++++ .../testsetups/ccip/test_helpers.go | 9 +- 5 files changed, 306 insertions(+), 5 deletions(-) create mode 100644 deployment/ccip/changeset/v1_5/cs_ocr2_config.go create mode 100644 deployment/ccip/changeset/v1_5/test_helpers.go diff --git a/deployment/ccip/changeset/v1_5/cs_add_lanes.go b/deployment/ccip/changeset/v1_5/cs_add_lanes.go index 46e79a589af..fbfe9fd3fa1 100644 --- a/deployment/ccip/changeset/v1_5/cs_add_lanes.go +++ b/deployment/ccip/changeset/v1_5/cs_add_lanes.go @@ -19,6 +19,8 @@ import ( integrationtesthelpers "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers/integration" ) +var _ deployment.ChangeSet[AddLanesConfig] = AddLanes + type AddLanesConfig struct { Configs []AddLaneConfig } diff --git a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go new file mode 100644 index 00000000000..ee7f7ff84f4 --- /dev/null +++ b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go @@ -0,0 +1,218 @@ +package v1_5 + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/deployment/common/types" + ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" +) + +type FinalOCR2Config struct { + Signers []common.Address + Transmitters []common.Address + F uint8 + OnchainConfig []byte + OffchainConfigVersion uint64 + OffchainConfig []byte +} + +type CommitOCR2ConfigParams struct { + DestinationChainSelector uint64 + SourceChainSelector uint64 + CommitOffchainConfig ccip.CommitOffchainConfig + OCR2ConfigParams confighelper.PublicConfig +} + +func (c CommitOCR2ConfigParams) SetCommitOffChainCfg() error { + cfgBytes, err := ccipconfig.EncodeOffchainConfig( + testhelpers.NewCommitOffchainConfig( + c.CommitOffchainConfig.GasPriceHeartBeat, + c.CommitOffchainConfig.GasPriceDeviationPPB, + )) +} + +func (c CommitOCR2ConfigParams) Validate(state changeset.CCIPOnChainState) error { + if err := deployment.IsValidChainSelector(c.DestinationChainSelector); err != nil { + return fmt.Errorf("invalid DestinationChainSelector: %w", err) + } + + chain, exists := state.Chains[c.DestinationChainSelector] + if !exists { + return fmt.Errorf("chain %d does not exist in state", c.DestinationChainSelector) + } + if chain.CommitStore == nil { + return fmt.Errorf("chain %d does not have a commit store", c.DestinationChainSelector) + } + _, exists = chain.CommitStore[c.SourceChainSelector] + if !exists { + return fmt.Errorf("chain %d does not have a commit store for source chain %d", c.DestinationChainSelector, c.SourceChainSelector) + } + // TODO : add validation for rest of the configs + return nil +} + +type ExecuteOCR2ConfigParams struct { + DestinationChainSelector uint64 + SourceChainSelector uint64 + ExecOffchainConfig ccip.ExecOffchainConfig + ExecOnchainConfig ccip.ExecOnchainConfig + OCR2ConfigParams types.OCRParameters +} + +func (e ExecuteOCR2ConfigParams) Validate(state changeset.CCIPOnChainState) error { + if err := e.OCR2ConfigParams.Validate(); err != nil { + return err + } + if err := e.ExecOnchainConfig.Validate(); err != nil { + return err + } + chain, exists := state.Chains[e.DestinationChainSelector] + if !exists { + return fmt.Errorf("chain %d does not exist in state", e.DestinationChainSelector) + } + if chain.EVM2EVMOffRamp == nil { + return fmt.Errorf("chain %d does not have an EVM2EVMOffRamp", e.DestinationChainSelector) + } + _, exists = chain.EVM2EVMOffRamp[e.SourceChainSelector] + if !exists { + return fmt.Errorf("chain %d does not have an EVM2EVMOffRamp for source chain %d", e.DestinationChainSelector, e.SourceChainSelector) + } + // TODO : add validation for rest of the configs + return nil +} + +type OCR2Config struct { + CommitConfigs []CommitOCR2ConfigParams + ExecConfigs []ExecuteOCR2ConfigParams +} + +func (o OCR2Config) Validate(state changeset.CCIPOnChainState) error { + for _, c := range o.CommitConfigs { + if err := c.Validate(state); err != nil { + return err + } + } + for _, e := range o.ExecConfigs { + if err := e.Validate(state); err != nil { + return err + } + } + return nil +} + +func DeriveOCR2Config( + env deployment.Environment, + chainSel uint64, + ocrParams confighelper.PublicConfig, +) (FinalOCR2Config, error) { + nodeInfo, err := deployment.NodeInfo(env.NodeIDs, env.Offchain) + if err != nil { + return FinalOCR2Config{}, fmt.Errorf("failed to get node info: %w", err) + } + nodes := nodeInfo.NonBootstraps() + // Get OCR3 Config from helper + var schedule []int + var oracles []confighelper.OracleIdentityExtra + for _, node := range nodes { + schedule = append(schedule, 1) + cfg, exists := node.OCRConfigForChainSelector(chainSel) + if !exists { + return FinalOCR2Config{}, fmt.Errorf("no OCR config for chain %d", chainSel) + } + oracles = append(oracles, confighelper.OracleIdentityExtra{ + OracleIdentity: confighelper.OracleIdentity{ + OnchainPublicKey: cfg.OnchainPublicKey, + TransmitAccount: cfg.TransmitAccount, + OffchainPublicKey: cfg.OffchainPublicKey, + PeerID: cfg.PeerID.String()[4:], + }, ConfigEncryptionPublicKey: cfg.ConfigEncryptionPublicKey, + }) + } + + signers, transmitters, threshold, onchainConfig, offchainConfigVersion, offchainConfig, err := confighelper.ContractSetConfigArgsForTests( + ocrParams.DeltaProgress, + ocrParams.DeltaResend, + ocrParams.DeltaRound, + ocrParams.DeltaGrace, + ocrParams.DeltaStage, + ocrParams.RMax, + schedule, + oracles, + ocrParams.ReportingPluginConfig, + nil, + ocrParams.MaxDurationQuery, + ocrParams.MaxDurationObservation, + ocrParams.MaxDurationReport, + ocrParams.MaxDurationShouldAcceptFinalizedReport, + ocrParams.MaxDurationShouldTransmitAcceptedReport, + ocrParams.F, + ocrParams.OnchainConfig, + ) + var signersAddresses []common.Address + for _, signer := range signers { + if len(signer) != 20 { + return FinalOCR2Config{}, fmt.Errorf("address is not 20 bytes %s", signer) + } + signersAddresses = append(signersAddresses, common.BytesToAddress(signer)) + } + var transmittersAddresses []common.Address + for _, transmitter := range transmitters { + bytes, err := hexutil.Decode(string(transmitter)) + if err != nil { + return FinalOCR2Config{}, errors.Wrap(err, fmt.Sprintf("given address is not valid %s", transmitter)) + } + if len(bytes) != 20 { + return FinalOCR2Config{}, errors.Errorf("address is not 20 bytes %s", transmitter) + } + transmittersAddresses = append(transmittersAddresses, common.BytesToAddress(bytes)) + } + return FinalOCR2Config{ + Signers: signersAddresses, + Transmitters: transmittersAddresses, + F: threshold, + OnchainConfig: onchainConfig, + OffchainConfigVersion: offchainConfigVersion, + OffchainConfig: offchainConfig, + }, nil +} + +// SetOCR2Config sets the OCR2 config on the chain for commit and offramp +// This is currently not suitable for prod environments it's only for testing +func SetOCR2Config(env deployment.Environment, c OCR2Config) (deployment.ChangesetOutput, error) { + state, err := changeset.LoadOnchainState(env) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to load CCIP onchain state: %w", err) + } + if err := c.Validate(state); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("invalid OCR2 config: %w", err) + } + for _, commit := range c.CommitConfigs { + if err := setOCR2ConfigCommit(env, state, commit); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to set OCR2 config commit: %w", err) + } + } + for _, exec := range c.ExecConfigs { + if err := setOCR2ConfigExec(env, state, exec); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to set OCR2 config exec: %w", err) + } + } + return deployment.ChangesetOutput{}, nil +} + +func setOCR2ConfigExec(env deployment.Environment, state changeset.CCIPOnChainState, c ExecuteOCR2ConfigParams) error { + + return nil +} + +func setOCR2ConfigCommit(env deployment.Environment, state changeset.CCIPOnChainState, c CommitOCR2ConfigParams) error { + return nil +} diff --git a/deployment/ccip/changeset/v1_5/e2e_test.go b/deployment/ccip/changeset/v1_5/e2e_test.go index 114343fd0d2..214e1724499 100644 --- a/deployment/ccip/changeset/v1_5/e2e_test.go +++ b/deployment/ccip/changeset/v1_5/e2e_test.go @@ -16,5 +16,5 @@ func TestE2ELegacy(t *testing.T) { Nodes: 4, Bootstraps: 1, }) - + // prep } diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go new file mode 100644 index 00000000000..f1e84a72296 --- /dev/null +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -0,0 +1,80 @@ +package v1_5 + +import ( + "math/big" + "testing" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" +) + +type TestConfig struct { + Chains int + NumOfUsersPerChain int + Nodes int + Bootstraps int +} + +func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.TestEnvironment) changeset.DeployedEnv { + var err error + lggr := logger.Test(t) + tEnv.StartChains(t, tc) + e := tEnv.DeployedEnvironment() + require.NotEmpty(t, e.Env.Chains) + ab := deployment.NewMemoryAddressBook() + tEnv.StartNodes(t, tc, deployment.CapabilityRegistryConfig{}) + e = tEnv.DeployedEnvironment() + allChains := e.Env.AllChainSelectors() + + mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) + for _, c := range e.Env.AllChainSelectors() { + mcmsCfg[c] = commontypes.MCMSWithTimelockConfig{ + Canceller: commonchangeset.SingleGroupMCMS(t), + Bypasser: commonchangeset.SingleGroupMCMS(t), + Proposer: commonchangeset.SingleGroupMCMS(t), + TimelockMinDelay: big.NewInt(0), + } + } + var prereqCfg []changeset.DeployPrerequisiteConfigPerChain + for _, chain := range allChains { + var opts []changeset.PrerequisiteOpt + if tc != nil { + if tc.IsUSDC { + opts = append(opts, changeset.WithUSDCEnabled()) + } + if tc.IsMultiCall3 { + opts = append(opts, changeset.WithMulticall3()) + } + } + opts = append(opts, changeset.WithLegacyDeployment(changeset.LegacyDeploymentConfig{ + PriceRegStalenessThreshold: 60 * 60 * 24 * 14, // two weeks + })) + prereqCfg = append(prereqCfg, changeset.DeployPrerequisiteConfigPerChain{ + ChainSelector: chain, + Opts: opts, + }) + } + + e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, []commonchangeset.ChangesetApplication{ + { + Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployLinkToken), + Config: allChains, + }, + { + Changeset: commonchangeset.WrapChangeSet(changeset.DeployPrerequisites), + Config: changeset.DeployPrerequisiteConfig{ + Configs: prereqCfg, + }, + }, + { + Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployMCMSWithTimelock), + Config: mcmsCfg, + }, + }) + require.NoError(t, err) +} diff --git a/integration-tests/testsetups/ccip/test_helpers.go b/integration-tests/testsetups/ccip/test_helpers.go index b859fab10c5..937c2283421 100644 --- a/integration-tests/testsetups/ccip/test_helpers.go +++ b/integration-tests/testsetups/ccip/test_helpers.go @@ -420,10 +420,11 @@ func StartChainlinkNodes( cfg.NodeConfig.CommonChainConfigTOML, cfg.NodeConfig.ChainConfigTOMLByChainID, ) - - toml.Capabilities.ExternalRegistry.NetworkID = ptr.Ptr(registryConfig.NetworkType) - toml.Capabilities.ExternalRegistry.ChainID = ptr.Ptr(strconv.FormatUint(registryConfig.EVMChainID, 10)) - toml.Capabilities.ExternalRegistry.Address = ptr.Ptr(registryConfig.Contract.String()) + if registryConfig.Contract != (common.Address{}) { + toml.Capabilities.ExternalRegistry.NetworkID = ptr.Ptr(registryConfig.NetworkType) + toml.Capabilities.ExternalRegistry.ChainID = ptr.Ptr(strconv.FormatUint(registryConfig.EVMChainID, 10)) + toml.Capabilities.ExternalRegistry.Address = ptr.Ptr(registryConfig.Contract.String()) + } if err != nil { return err From 9b85a7bb31142a4143ecf2d4625a23977c9320b6 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Mon, 16 Dec 2024 16:18:59 -0800 Subject: [PATCH 11/29] add test --- .../ccip/changeset/v1_5/cs_ocr2_config.go | 215 +++++++--- deployment/ccip/changeset/v1_5/e2e_test.go | 36 +- .../ccip/changeset/v1_5/test_helpers.go | 380 +++++++++++++++++- 3 files changed, 557 insertions(+), 74 deletions(-) diff --git a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go index ee7f7ff84f4..d7b22f26c8f 100644 --- a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go +++ b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go @@ -6,12 +6,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - "github.com/smartcontractkit/chainlink/deployment/common/types" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" ) @@ -28,22 +29,48 @@ type FinalOCR2Config struct { type CommitOCR2ConfigParams struct { DestinationChainSelector uint64 SourceChainSelector uint64 - CommitOffchainConfig ccip.CommitOffchainConfig OCR2ConfigParams confighelper.PublicConfig + GasPriceHeartBeat config.Duration + DAGasPriceDeviationPPB uint32 + ExecGasPriceDeviationPPB uint32 + TokenPriceHeartBeat config.Duration + TokenPriceDeviationPPB uint32 + InflightCacheExpiry config.Duration + PriceReportingDisabled bool } -func (c CommitOCR2ConfigParams) SetCommitOffChainCfg() error { +func (c CommitOCR2ConfigParams) PopulateOffChainAndOnChainCfg(priceReg common.Address) error { cfgBytes, err := ccipconfig.EncodeOffchainConfig( testhelpers.NewCommitOffchainConfig( - c.CommitOffchainConfig.GasPriceHeartBeat, - c.CommitOffchainConfig.GasPriceDeviationPPB, - )) + c.GasPriceHeartBeat, + c.DAGasPriceDeviationPPB, + c.ExecGasPriceDeviationPPB, + c.TokenPriceHeartBeat, + c.TokenPriceDeviationPPB, + c.InflightCacheExpiry, + c.PriceReportingDisabled, + ), + ) + if err != nil { + return errors.Wrapf(err, "failed to encode offchain config for source chain %d and destination chain %d", + c.SourceChainSelector, c.DestinationChainSelector) + } + c.OCR2ConfigParams.ReportingPluginConfig = cfgBytes + c.OCR2ConfigParams.OnchainConfig, err = abihelpers.EncodeAbiStruct(testhelpers.NewCommitOnchainConfig(priceReg)) + if err != nil { + return fmt.Errorf("failed to encode onchain config for source chain %d and destination chain %d: %w", + c.SourceChainSelector, c.DestinationChainSelector, err) + } + return nil } func (c CommitOCR2ConfigParams) Validate(state changeset.CCIPOnChainState) error { if err := deployment.IsValidChainSelector(c.DestinationChainSelector); err != nil { return fmt.Errorf("invalid DestinationChainSelector: %w", err) } + if err := deployment.IsValidChainSelector(c.SourceChainSelector); err != nil { + return fmt.Errorf("invalid SourceChainSelector: %w", err) + } chain, exists := state.Chains[c.DestinationChainSelector] if !exists { @@ -56,25 +83,67 @@ func (c CommitOCR2ConfigParams) Validate(state changeset.CCIPOnChainState) error if !exists { return fmt.Errorf("chain %d does not have a commit store for source chain %d", c.DestinationChainSelector, c.SourceChainSelector) } - // TODO : add validation for rest of the configs + if chain.PriceRegistry == nil { + return fmt.Errorf("chain %d does not have a price registry", c.DestinationChainSelector) + } return nil } type ExecuteOCR2ConfigParams struct { - DestinationChainSelector uint64 - SourceChainSelector uint64 - ExecOffchainConfig ccip.ExecOffchainConfig - ExecOnchainConfig ccip.ExecOnchainConfig - OCR2ConfigParams types.OCRParameters + DestinationChainSelector uint64 + SourceChainSelector uint64 + DestOptimisticConfirmations uint32 + BatchGasLimit uint32 + RelativeBoostPerWaitHour float64 + InflightCacheExpiry config.Duration + RootSnoozeTime config.Duration + BatchingStrategyID uint32 + MessageVisibilityInterval config.Duration + ExecOnchainConfig evm_2_evm_offramp.EVM2EVMOffRampDynamicConfig + OCR2ConfigParams confighelper.PublicConfig +} + +func (c ExecuteOCR2ConfigParams) PopulateOffChainAndOnChainCfg(router, priceReg common.Address) error { + var err error + c.OCR2ConfigParams.ReportingPluginConfig, err = testhelpers.NewExecOffchainConfig( + c.DestOptimisticConfirmations, + c.BatchGasLimit, + c.RelativeBoostPerWaitHour, + c.InflightCacheExpiry, + c.RootSnoozeTime, + c.BatchingStrategyID, + ).Encode() + if err != nil { + return fmt.Errorf("failed to encode offchain config for exec plugin, source chain %d dest chain %d :%w", + c.SourceChainSelector, c.DestinationChainSelector, err) + } + c.OCR2ConfigParams.OnchainConfig, err = abihelpers.EncodeAbiStruct(testhelpers.NewExecOnchainConfig( + c.ExecOnchainConfig.PermissionLessExecutionThresholdSeconds, + router, + priceReg, + c.ExecOnchainConfig.MaxNumberOfTokensPerMsg, + c.ExecOnchainConfig.MaxDataBytes, + )) + if err != nil { + return fmt.Errorf("failed to encode onchain config for exec plugin, source chain %d dest chain %d :%w", + c.SourceChainSelector, c.DestinationChainSelector, err) + } + return nil } func (e ExecuteOCR2ConfigParams) Validate(state changeset.CCIPOnChainState) error { - if err := e.OCR2ConfigParams.Validate(); err != nil { + if err := e.ExecOnchainConfig.Validate(); err != nil { return err } - if err := e.ExecOnchainConfig.Validate(); err != nil { + if err := e.ExecOffchainConfig.Validate(); err != nil { return err } + if err := deployment.IsValidChainSelector(e.SourceChainSelector); err != nil { + return fmt.Errorf("invalid SourceChainSelector: %w", err) + } + if err := deployment.IsValidChainSelector(e.DestinationChainSelector); err != nil { + return fmt.Errorf("invalid DestinationChainSelector: %w", err) + } chain, exists := state.Chains[e.DestinationChainSelector] if !exists { return fmt.Errorf("chain %d does not exist in state", e.DestinationChainSelector) @@ -86,7 +155,12 @@ func (e ExecuteOCR2ConfigParams) Validate(state changeset.CCIPOnChainState) erro if !exists { return fmt.Errorf("chain %d does not have an EVM2EVMOffRamp for source chain %d", e.DestinationChainSelector, e.SourceChainSelector) } - // TODO : add validation for rest of the configs + if chain.PriceRegistry == nil { + return fmt.Errorf("chain %d does not have a price registry", e.DestinationChainSelector) + } + if chain.Router == nil { + return fmt.Errorf("chain %d does not have a router", e.DestinationChainSelector) + } return nil } @@ -109,7 +183,80 @@ func (o OCR2Config) Validate(state changeset.CCIPOnChainState) error { return nil } -func DeriveOCR2Config( +// SetOCR2ConfigForTest sets the OCR2 config on the chain for commit and offramp +// This is currently not suitable for prod environments it's only for testing +func SetOCR2ConfigForTest(env deployment.Environment, c OCR2Config) (deployment.ChangesetOutput, error) { + state, err := changeset.LoadOnchainState(env) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to load CCIP onchain state: %w", err) + } + if err := c.Validate(state); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("invalid OCR2 config: %w", err) + } + for _, commit := range c.CommitConfigs { + if err := commit.PopulateOffChainAndOnChainCfg(state.Chains[commit.DestinationChainSelector].PriceRegistry.Address()); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to populate offchain and onchain config for commit: %w", err) + } + finalCfg, err := deriveOCR2Config(env, commit.DestinationChainSelector, commit.OCR2ConfigParams) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to derive OCR2 config for commit: %w", err) + } + commitStore := state.Chains[commit.DestinationChainSelector].CommitStore[commit.SourceChainSelector] + chain := env.Chains[commit.DestinationChainSelector] + tx, err := commitStore.SetOCR2Config( + chain.DeployerKey, + finalCfg.Signers, + finalCfg.Transmitters, + finalCfg.F, + finalCfg.OnchainConfig, + finalCfg.OffchainConfigVersion, + finalCfg.OffchainConfig, + ) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to set OCR2 config for commit store %s on chain %s: %w", + commitStore.Address().String(), chain.String(), err) + } + _, err = chain.Confirm(tx) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm OCR2 for commit store %s config on chain %s: %w", + commitStore.Address().String(), chain.String(), err) + } + } + for _, exec := range c.ExecConfigs { + if err := exec.PopulateOffChainAndOnChainCfg( + state.Chains[exec.DestinationChainSelector].Router.Address(), + state.Chains[exec.DestinationChainSelector].PriceRegistry.Address()); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to populate offchain and onchain config for offramp: %w", err) + } + finalCfg, err := deriveOCR2Config(env, exec.DestinationChainSelector, exec.OCR2ConfigParams) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to derive OCR2 config for offramp: %w", err) + } + offRamp := state.Chains[exec.DestinationChainSelector].EVM2EVMOffRamp[exec.SourceChainSelector] + chain := env.Chains[exec.DestinationChainSelector] + tx, err := offRamp.SetOCR2Config( + chain.DeployerKey, + finalCfg.Signers, + finalCfg.Transmitters, + finalCfg.F, + finalCfg.OnchainConfig, + finalCfg.OffchainConfigVersion, + finalCfg.OffchainConfig, + ) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to set OCR2 config for offramp %s on chain %s: %w", + offRamp.Address().String(), chain.String(), err) + } + _, err = chain.Confirm(tx) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm OCR2 for offramp %s config on chain %s: %w", + offRamp.Address().String(), chain.String(), err) + } + } + return deployment.ChangesetOutput{}, nil +} + +func deriveOCR2Config( env deployment.Environment, chainSel uint64, ocrParams confighelper.PublicConfig, @@ -154,7 +301,7 @@ func DeriveOCR2Config( ocrParams.MaxDurationReport, ocrParams.MaxDurationShouldAcceptFinalizedReport, ocrParams.MaxDurationShouldTransmitAcceptedReport, - ocrParams.F, + int(nodes.DefaultF()), ocrParams.OnchainConfig, ) var signersAddresses []common.Address @@ -184,35 +331,3 @@ func DeriveOCR2Config( OffchainConfig: offchainConfig, }, nil } - -// SetOCR2Config sets the OCR2 config on the chain for commit and offramp -// This is currently not suitable for prod environments it's only for testing -func SetOCR2Config(env deployment.Environment, c OCR2Config) (deployment.ChangesetOutput, error) { - state, err := changeset.LoadOnchainState(env) - if err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("failed to load CCIP onchain state: %w", err) - } - if err := c.Validate(state); err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("invalid OCR2 config: %w", err) - } - for _, commit := range c.CommitConfigs { - if err := setOCR2ConfigCommit(env, state, commit); err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("failed to set OCR2 config commit: %w", err) - } - } - for _, exec := range c.ExecConfigs { - if err := setOCR2ConfigExec(env, state, exec); err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("failed to set OCR2 config exec: %w", err) - } - } - return deployment.ChangesetOutput{}, nil -} - -func setOCR2ConfigExec(env deployment.Environment, state changeset.CCIPOnChainState, c ExecuteOCR2ConfigParams) error { - - return nil -} - -func setOCR2ConfigCommit(env deployment.Environment, state changeset.CCIPOnChainState, c CommitOCR2ConfigParams) error { - return nil -} diff --git a/deployment/ccip/changeset/v1_5/e2e_test.go b/deployment/ccip/changeset/v1_5/e2e_test.go index 214e1724499..36cdbcccdb7 100644 --- a/deployment/ccip/changeset/v1_5/e2e_test.go +++ b/deployment/ccip/changeset/v1_5/e2e_test.go @@ -3,18 +3,34 @@ package v1_5 import ( "testing" - "go.uber.org/zap/zapcore" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/deployment/environment/memory" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" ) func TestE2ELegacy(t *testing.T) { - e := memory.NewMemoryEnvironment(t, logger.TestLogger(t), zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ - Chains: 2, - NumOfUsersPerChain: 1, - Nodes: 4, - Bootstraps: 1, - }) - // prep + e := NewMemoryEnvironment(t) + state, err := changeset.LoadOnchainState(e.Env) + require.NoError(t, err) + allChains := e.Env.AllChainSelectors() + src, dest := allChains[0], allChains[1] + srcChain := e.Env.Chains[src] + destChain := e.Env.Chains[dest] + sentEvent, err := SendRequest(t, e.Env, state, + changeset.WithSourceChain(src), + changeset.WithDestChain(dest), + changeset.WithTestRouter(false), + changeset.WithEvm2AnyMessage(router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32), + Data: []byte("hello"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }), + ) + require.NoError(t, err) + require.NotNil(t, sentEvent) + WaitForCommit(t, srcChain, destChain, state.Chains[dest].CommitStore[src], sentEvent.Message.SequenceNumber) } diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index f1e84a72296..73c8a455e2a 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -1,44 +1,57 @@ package v1_5 import ( + "context" + "fmt" "math/big" + "net/http" + "net/http/httptest" "testing" + "time" - "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/config" + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" + "github.com/smartcontractkit/chainlink/deployment/environment/memory" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" ) -type TestConfig struct { - Chains int - NumOfUsersPerChain int - Nodes int - Bootstraps int +// NewMemoryEnvironment creates an in-memory environment based on the testconfig requested +func NewMemoryEnvironment(t *testing.T, opts ...changeset.TestOps) changeset.DeployedEnv { + testCfg := changeset.DefaultTestConfigs() + for _, opt := range opts { + opt(testCfg) + } + require.NoError(t, testCfg.Validate(), "invalid test config") + env := &changeset.MemoryEnvironment{} + return NewEnvironment(t, testCfg, env) } func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.TestEnvironment) changeset.DeployedEnv { var err error - lggr := logger.Test(t) tEnv.StartChains(t, tc) e := tEnv.DeployedEnvironment() require.NotEmpty(t, e.Env.Chains) - ab := deployment.NewMemoryAddressBook() tEnv.StartNodes(t, tc, deployment.CapabilityRegistryConfig{}) e = tEnv.DeployedEnvironment() allChains := e.Env.AllChainSelectors() mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) for _, c := range e.Env.AllChainSelectors() { - mcmsCfg[c] = commontypes.MCMSWithTimelockConfig{ - Canceller: commonchangeset.SingleGroupMCMS(t), - Bypasser: commonchangeset.SingleGroupMCMS(t), - Proposer: commonchangeset.SingleGroupMCMS(t), - TimelockMinDelay: big.NewInt(0), - } + mcmsCfg[c] = proposalutils.SingleGroupTimelockConfig(t) } var prereqCfg []changeset.DeployPrerequisiteConfigPerChain for _, chain := range allChains { @@ -77,4 +90,343 @@ func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.Test }, }) require.NoError(t, err) + state, err := changeset.LoadOnchainState(e.Env) + require.NoError(t, err) + var pairs []changeset.SourceDestPair + for _, src := range allChains { + for _, dest := range allChains { + if src != dest { + pairs = append(pairs, changeset.SourceDestPair{ + SourceChainSelector: src, + DestChainSelector: dest, + }) + } + } + } + addLanesCfg, commitOCR2Configs, execOCR2Configs := LaneConfigsForChains(t, state, pairs) + e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, []commonchangeset.ChangesetApplication{ + { + Changeset: commonchangeset.WrapChangeSet(AddLanes), + Config: AddLanesConfig{ + Configs: addLanesCfg, + }, + }, + { + Changeset: commonchangeset.WrapChangeSet(SetOCR2ConfigForTest), + Config: OCR2Config{ + CommitConfigs: commitOCR2Configs, + ExecConfigs: execOCR2Configs, + }, + }, + }) + require.NoError(t, err) + return e +} + +func LaneConfigsForChains(t *testing.T, state changeset.CCIPOnChainState, pairs []changeset.SourceDestPair) ( + []AddLaneConfig, + []CommitOCR2ConfigParams, + []ExecuteOCR2ConfigParams, +) { + var addLanesCfg []AddLaneConfig + var commitOCR2Configs []CommitOCR2ConfigParams + var execOCR2Configs []ExecuteOCR2ConfigParams + for _, pair := range pairs { + dest := pair.DestChainSelector + src := pair.SourceChainSelector + sourceChainState := state.Chains[src] + destChainState := state.Chains[dest] + require.NotNil(t, sourceChainState.LinkToken) + require.NotNil(t, sourceChainState.RMNProxy) + require.NotNil(t, sourceChainState.TokenAdminRegistry) + require.NotNil(t, sourceChainState.Router) + require.NotNil(t, sourceChainState.PriceRegistry) + require.NotNil(t, sourceChainState.Weth9) + require.NotNil(t, destChainState.LinkToken) + require.NotNil(t, destChainState.RMNProxy) + require.NotNil(t, destChainState.TokenAdminRegistry) + tokenPrice, _, _ := CreatePricesPipeline(t, state, src, dest) + addLanesCfg = append(addLanesCfg, AddLaneConfig{ + SourceChainSelector: src, + DestinationChainSelector: dest, + OnRampStaticCfg: evm_2_evm_onramp.EVM2EVMOnRampStaticConfig{ + LinkToken: sourceChainState.LinkToken.Address(), + ChainSelector: src, + DestChainSelector: dest, + DefaultTxGasLimit: 200_000, + MaxNopFeesJuels: big.NewInt(0).Mul(big.NewInt(100_000_000), big.NewInt(1e18)), + PrevOnRamp: common.Address{}, + RmnProxy: sourceChainState.RMNProxy.Address(), + TokenAdminRegistry: sourceChainState.TokenAdminRegistry.Address(), + }, + OnRampDynamicCfg: evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{ + Router: sourceChainState.Router.Address(), + MaxNumberOfTokensPerMsg: 5, + DestGasOverhead: 350_000, + DestGasPerPayloadByte: 16, + DestDataAvailabilityOverheadGas: 33_596, + DestGasPerDataAvailabilityByte: 16, + DestDataAvailabilityMultiplierBps: 6840, + PriceRegistry: sourceChainState.PriceRegistry.Address(), + MaxDataBytes: 1e5, + MaxPerMsgGasLimit: 4_000_000, + DefaultTokenFeeUSDCents: 50, + DefaultTokenDestGasOverhead: 125_000, + }, + OnRampFeeTokenArgs: []evm_2_evm_onramp.EVM2EVMOnRampFeeTokenConfigArgs{ + { + Token: sourceChainState.LinkToken.Address(), + NetworkFeeUSDCents: 1_00, + GasMultiplierWeiPerEth: 1e18, + PremiumMultiplierWeiPerEth: 9e17, + Enabled: true, + }, + { + Token: sourceChainState.Weth9.Address(), + NetworkFeeUSDCents: 1_00, + GasMultiplierWeiPerEth: 1e18, + PremiumMultiplierWeiPerEth: 1e18, + Enabled: true, + }, + }, + OnRampTransferTokenCfgs: []evm_2_evm_onramp.EVM2EVMOnRampTokenTransferFeeConfigArgs{ + { + Token: sourceChainState.LinkToken.Address(), + MinFeeUSDCents: 50, // $0.5 + MaxFeeUSDCents: 1_000_000_00, // $ 1 million + DeciBps: 5_0, // 5 bps + DestGasOverhead: 350_000, + DestBytesOverhead: 32, + AggregateRateLimitEnabled: true, + }, + }, + OnRampNopsAndWeight: []evm_2_evm_onramp.EVM2EVMOnRampNopAndWeight{}, + OnRampRateLimiterCfg: evm_2_evm_onramp.RateLimiterConfig{ + IsEnabled: true, + Capacity: testhelpers.LinkUSDValue(100), + Rate: testhelpers.LinkUSDValue(1), + }, + OffRampRateLimiterCfg: evm_2_evm_offramp.RateLimiterConfig{ + IsEnabled: true, + Capacity: testhelpers.LinkUSDValue(100), + Rate: testhelpers.LinkUSDValue(1), + }, + InitialTokenPrices: []price_registry_1_2_0.InternalTokenPriceUpdate{ + { + SourceToken: sourceChainState.LinkToken.Address(), + UsdPerToken: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(20)), + }, + { + SourceToken: sourceChainState.Weth9.Address(), + UsdPerToken: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(2000)), + }, + }, + GasPriceUpdates: []price_registry_1_2_0.InternalGasPriceUpdate{ + { + DestChainSelector: dest, + UsdPerUnitGas: big.NewInt(20000e9), + }, + }, + TokenPricesUSDPipeline: tokenPrice, + }) + commitOCR2Configs = append(commitOCR2Configs, CommitOCR2ConfigParams{ + SourceChainSelector: src, + DestinationChainSelector: dest, + OCR2ConfigParams: DefaultOCRParams(), + GasPriceHeartBeat: *config.MustNewDuration(10 * time.Second), + DAGasPriceDeviationPPB: 1, + ExecGasPriceDeviationPPB: 1, + TokenPriceHeartBeat: *config.MustNewDuration(10 * time.Second), + TokenPriceDeviationPPB: 1, + InflightCacheExpiry: *config.MustNewDuration(5 * time.Second), + PriceReportingDisabled: false, + }) + execOCR2Configs = append(execOCR2Configs, ExecuteOCR2ConfigParams{ + DestinationChainSelector: dest, + SourceChainSelector: src, + DestOptimisticConfirmations: 1, + BatchGasLimit: 5_000_000, + RelativeBoostPerWaitHour: 0.07, + InflightCacheExpiry: *config.MustNewDuration(1 * time.Minute), + RootSnoozeTime: *config.MustNewDuration(1 * time.Minute), + BatchingStrategyID: 0, + MessageVisibilityInterval: config.Duration{}, + ExecOnchainConfig: evm_2_evm_offramp.EVM2EVMOffRampDynamicConfig{ + PermissionLessExecutionThresholdSeconds: uint32(24 * time.Hour.Seconds()), + MaxDataBytes: 1e5, + MaxNumberOfTokensPerMsg: 5, + }, + OCR2ConfigParams: DefaultOCRParams(), + }) + } + return addLanesCfg, commitOCR2Configs, execOCR2Configs +} + +func CreatePricesPipeline(t *testing.T, state changeset.CCIPOnChainState, source, dest uint64) (string, *httptest.Server, *httptest.Server) { + sourceRouter := state.Chains[source].Router + destRouter := state.Chains[dest].Router + destLink := state.Chains[dest].LinkToken + linkUSD := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, err := w.Write([]byte(`{"UsdPerLink": "8000000000000000000"}`)) + require.NoError(t, err) + })) + t.Cleanup(linkUSD.Close) + + ethUSD := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, err := w.Write([]byte(`{"UsdPerETH": "1700000000000000000000"}`)) + require.NoError(t, err) + })) + t.Cleanup(ethUSD.Close) + + sourceWrappedNative, err := sourceRouter.GetWrappedNative(nil) + require.NoError(t, err) + destWrappedNative, err := destRouter.GetWrappedNative(nil) + require.NoError(t, err) + tokenPricesUSDPipeline := fmt.Sprintf(` +// Price 1 +link [type=http method=GET url="%s"]; +link_parse [type=jsonparse path="UsdPerLink"]; +link->link_parse; +eth [type=http method=GET url="%s"]; +eth_parse [type=jsonparse path="UsdPerETH"]; +eth->eth_parse; +merge [type=merge left="{}" right="{\\\"%s\\\":$(link_parse), \\\"%s\\\":$(eth_parse), \\\"%s\\\":$(eth_parse)}"];`, + linkUSD.URL, ethUSD.URL, destLink.Address(), sourceWrappedNative, destWrappedNative) + + return tokenPricesUSDPipeline, linkUSD, ethUSD +} + +func DefaultOCRParams() confighelper.PublicConfig { + return confighelper.PublicConfig{ + DeltaProgress: 2 * time.Second, + DeltaResend: 1 * time.Second, + DeltaRound: 1 * time.Second, + DeltaGrace: 500 * time.Millisecond, + DeltaStage: 2 * time.Second, + RMax: 3, + MaxDurationInitialization: nil, + MaxDurationQuery: 50 * time.Millisecond, + MaxDurationObservation: 1 * time.Second, + MaxDurationReport: 100 * time.Millisecond, + MaxDurationShouldAcceptFinalizedReport: 100 * time.Millisecond, + MaxDurationShouldTransmitAcceptedReport: 100 * time.Millisecond, + } +} + +func SendRequest( + t *testing.T, + e deployment.Environment, + state changeset.CCIPOnChainState, + opts ...changeset.SendReqOpts, +) (*evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested, error) { + cfg := &changeset.CCIPSendReqConfig{} + for _, opt := range opts { + opt(cfg) + } + // Set default sender if not provided + if cfg.Sender == nil { + cfg.Sender = e.Chains[cfg.SourceChain].DeployerKey + } + t.Logf("Sending CCIP request from chain selector %d to chain selector %d from sender %s", + cfg.SourceChain, cfg.DestChain, cfg.Sender.From.String()) + tx, blockNum, err := changeset.CCIPSendRequest(e, state, cfg) + if err != nil { + return nil, err + } + + onRamp := state.Chains[cfg.SourceChain].EVM2EVMOnRamp[cfg.DestChain] + + it, err := onRamp.FilterCCIPSendRequested(&bind.FilterOpts{ + Start: blockNum, + End: &blockNum, + Context: context.Background(), + }) + if err != nil { + return nil, err + } + + require.True(t, it.Next()) + t.Logf("CCIP message (id %x) sent from chain selector %d to chain selector %d tx %s seqNum %d nonce %d sender %s", + it.Event.Message.MessageId[:], + cfg.SourceChain, + cfg.DestChain, + tx.Hash().String(), + it.Event.Message.SequenceNumber, + it.Event.Message.Sender.String(), + ) + return it.Event, nil +} + +func WaitForCommit( + t *testing.T, + src deployment.Chain, + dest deployment.Chain, + commitStore *commit_store.CommitStore, + seqNr uint64, +) { + timer := time.NewTimer(5 * time.Minute) + defer timer.Stop() + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + if backend, ok := src.Client.(*memory.Backend); ok { + backend.Commit() + } + if backend, ok := dest.Client.(*memory.Backend); ok { + backend.Commit() + } + minSeqNr, err := commitStore.GetExpectedNextSequenceNumber(nil) + require.NoError(t, err) + t.Logf("Waiting for commit for sequence number %d, current min sequence number %d", seqNr, minSeqNr) + if minSeqNr > seqNr { + t.Logf("Commit for sequence number %d found", seqNr) + return + } + case <-timer.C: + t.Fatalf("timed out waiting for commit for sequence number %d for commit store %s ", seqNr, commitStore.Address().String()) + return + } + } +} + +func WaitForExecute( + t *testing.T, + src deployment.Chain, + dest deployment.Chain, + offRamp *evm_2_evm_offramp.EVM2EVMOffRamp, + seqNrs []uint64, + blockNum uint64, +) { + timer := time.NewTimer(5 * time.Minute) + defer timer.Stop() + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + if backend, ok := src.Client.(*memory.Backend); ok { + backend.Commit() + } + if backend, ok := dest.Client.(*memory.Backend); ok { + backend.Commit() + } + it, err := offRamp.FilterExecutionStateChanged( + &bind.FilterOpts{ + Start: blockNum, + }, seqNrs, [][32]byte{}) + require.NoError(t, err) + for it.Next() { + t.Logf("Execution state changed for sequence number=%d current state=%d", it.Event.SequenceNumber, it.Event.State) + if cciptypes.MessageExecutionState(it.Event.State) == cciptypes.ExecutionStateSuccess { + t.Logf("Execution for sequence number %d found", it.Event.SequenceNumber) + return + } + } + case <-timer.C: + t.Fatalf("timed out waiting for execute for sequence numbers %v for offramp %s ", seqNrs, offRamp.Address().String()) + return + } + } } From cb923b089bb7e40d2468c23596f8ff002fa57b68 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Mon, 16 Dec 2024 18:29:21 -0800 Subject: [PATCH 12/29] more updates --- deployment/ccip/changeset/cs_deploy_chain.go | 20 --- deployment/ccip/changeset/cs_prerequisites.go | 20 +++ deployment/ccip/changeset/v1_5/cs_jobspec.go | 160 ++++++++++++++++++ .../{cs_add_lanes.go => cs_lane_contracts.go} | 124 ++------------ .../ccip/changeset/v1_5/cs_ocr2_config.go | 39 ++--- deployment/ccip/changeset/v1_5/e2e_test.go | 4 + .../ccip/changeset/v1_5/test_helpers.go | 81 +++++++-- deployment/environment.go | 3 + deployment/environment/memory/environment.go | 10 +- deployment/go.mod | 1 + deployment/go.sum | 4 +- 11 files changed, 293 insertions(+), 173 deletions(-) create mode 100644 deployment/ccip/changeset/v1_5/cs_jobspec.go rename deployment/ccip/changeset/v1_5/{cs_add_lanes.go => cs_lane_contracts.go} (67%) diff --git a/deployment/ccip/changeset/cs_deploy_chain.go b/deployment/ccip/changeset/cs_deploy_chain.go index e403481b001..444f204dd0a 100644 --- a/deployment/ccip/changeset/cs_deploy_chain.go +++ b/deployment/ccip/changeset/cs_deploy_chain.go @@ -14,7 +14,6 @@ import ( "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_home" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" "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" @@ -181,25 +180,6 @@ func deployChainContracts( e.Logger.Errorw("RMNProxy not found", "chain", chain.String()) return fmt.Errorf("rmn proxy not found for chain %s, deploy the prerequisites first", chain.String()) } - if chainState.Receiver == nil { - _, err := deployment.DeployContract(e.Logger, chain, ab, - func(chain deployment.Chain) deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver] { - receiverAddr, tx, receiver, err2 := maybe_revert_message_receiver.DeployMaybeRevertMessageReceiver( - chain.DeployerKey, - chain.Client, - false, - ) - return deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver]{ - receiverAddr, receiver, tx, deployment.NewTypeAndVersion(CCIPReceiver, deployment.Version1_0_0), err2, - } - }) - if err != nil { - e.Logger.Errorw("Failed to deploy receiver", "chain", chain.String(), "err", err) - return err - } - } else { - e.Logger.Infow("receiver already deployed", "addr", chainState.Receiver.Address, "chain", chain.String()) - } var rmnLegacyAddr common.Address if chainState.MockRMN != nil { rmnLegacyAddr = chainState.MockRMN.Address() diff --git a/deployment/ccip/changeset/cs_prerequisites.go b/deployment/ccip/changeset/cs_prerequisites.go index 919eb42f8ef..b811be3be47 100644 --- a/deployment/ccip/changeset/cs_prerequisites.go +++ b/deployment/ccip/changeset/cs_prerequisites.go @@ -9,6 +9,7 @@ import ( "golang.org/x/sync/errgroup" "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" @@ -384,6 +385,25 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address "messenger", messenger.Address(), ) } + if chainState.Receiver == nil { + _, err := deployment.DeployContract(e.Logger, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver] { + receiverAddr, tx, receiver, err2 := maybe_revert_message_receiver.DeployMaybeRevertMessageReceiver( + chain.DeployerKey, + chain.Client, + false, + ) + return deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver]{ + receiverAddr, receiver, tx, deployment.NewTypeAndVersion(CCIPReceiver, deployment.Version1_0_0), err2, + } + }) + if err != nil { + e.Logger.Errorw("Failed to deploy receiver", "chain", chain.String(), "err", err) + return err + } + } else { + e.Logger.Infow("receiver already deployed", "addr", chainState.Receiver.Address, "chain", chain.String()) + } // Only applicable if setting up for 1.5 version, remove this once we have fully migrated to 1.6 if deployOpts.LegacyDeploymentCfg != nil { if chainState.PriceRegistry == nil { diff --git a/deployment/ccip/changeset/v1_5/cs_jobspec.go b/deployment/ccip/changeset/v1_5/cs_jobspec.go new file mode 100644 index 00000000000..46ba50eec63 --- /dev/null +++ b/deployment/ccip/changeset/v1_5/cs_jobspec.go @@ -0,0 +1,160 @@ +package v1_5 + +import ( + "fmt" + "strconv" + + chain_selectors "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" + integrationtesthelpers "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers/integration" +) + +type JobSpecsForLanesConfig struct { + Configs []JobSpecInput +} + +func (c JobSpecsForLanesConfig) Validate() error { + for _, cfg := range c.Configs { + if err := cfg.Validate(); err != nil { + return fmt.Errorf("invalid JobSpecInput: %w", err) + } + } + return nil +} + +type JobSpecInput struct { + SourceChainSelector uint64 + DestinationChainSelector uint64 + DestinationStartBlock uint64 + TokenPricesUSDPipeline string + PriceGetterConfigJson string + USDCAttestationAPI string + USDCCfg *config.USDCConfig +} + +func (j JobSpecInput) Validate() error { + if err := deployment.IsValidChainSelector(j.SourceChainSelector); err != nil { + return fmt.Errorf("SourceChainSelector is invalid: %w", err) + } + if err := deployment.IsValidChainSelector(j.DestinationChainSelector); err != nil { + return fmt.Errorf("DestinationChainSelector is invalid: %w", err) + } + if j.TokenPricesUSDPipeline == "" && j.PriceGetterConfigJson == "" { + return fmt.Errorf("TokenPricesUSDPipeline or PriceGetterConfigJson is required") + } + if j.USDCCfg != nil { + if err := j.USDCCfg.ValidateUSDCConfig(); err != nil { + return fmt.Errorf("USDCCfg is invalid: %w", err) + } + if j.USDCAttestationAPI == "" { + return fmt.Errorf("USDCAttestationAPI is required") + } + } + return nil +} + +func JobSpecsForLanes(env deployment.Environment, c JobSpecsForLanesConfig) (deployment.ChangesetOutput, error) { + if err := c.Validate(); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("invalid JobSpecsForLanesConfig: %w", err) + } + state, err := changeset.LoadOnchainState(env) + if err != nil { + return deployment.ChangesetOutput{}, err + } + nodesToJobSpecs, err := jobSpecsForLane(env, state, c) + if err != nil { + return deployment.ChangesetOutput{}, err + } + return deployment.ChangesetOutput{ + JobSpecs: nodesToJobSpecs, + }, nil +} + +func jobSpecsForLane( + env deployment.Environment, + state changeset.CCIPOnChainState, + lanesCfg JobSpecsForLanesConfig, +) (map[string][]string, error) { + nodes, err := deployment.NodeInfo(env.NodeIDs, env.Offchain) + if err != nil { + return nil, err + } + nodesToJobSpecs := make(map[string][]string) + for _, node := range nodes { + var specs []string + for _, cfg := range lanesCfg.Configs { + var err error + destChainState := state.Chains[cfg.DestinationChainSelector] + sourceChain := env.Chains[cfg.SourceChainSelector] + destChain := env.Chains[cfg.DestinationChainSelector] + destEVMChainIdStr, err := chain_selectors.GetChainIDFromSelector(cfg.DestinationChainSelector) + if err != nil { + return nil, fmt.Errorf("failed to get chain ID from selector for chain %s: %w", destChain.String(), err) + } + destEVMChainId, err := strconv.ParseUint(destEVMChainIdStr, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse chain ID %s for chain %s: %w", destEVMChainIdStr, destChain.String(), err) + } + destChainDetails, err := chain_selectors.GetChainDetailsByChainIDAndFamily(destEVMChainIdStr, chain_selectors.FamilyEVM) + if err != nil { + return nil, fmt.Errorf("failed to get chain details for chain ID %s: %w", destEVMChainIdStr, err) + } + ccipJobParam := integrationtesthelpers.CCIPJobSpecParams{ + OffRamp: destChainState.EVM2EVMOffRamp[cfg.SourceChainSelector].Address(), + CommitStore: destChainState.CommitStore[cfg.SourceChainSelector].Address(), + SourceChainName: sourceChain.Name(), + DestChainName: destChain.Name(), + DestEvmChainId: destEVMChainId, + TokenPricesUSDPipeline: cfg.TokenPricesUSDPipeline, + PriceGetterConfig: cfg.PriceGetterConfigJson, + DestStartBlock: cfg.DestinationStartBlock, + USDCAttestationAPI: cfg.USDCAttestationAPI, + USDCConfig: cfg.USDCCfg, + P2PV2Bootstrappers: nodes.BootstrapLocators(), + } + if !node.IsBootstrap { + ocrCfg := node.SelToOCRConfig[destChainDetails] + ocrKeyBundleID := ocrCfg.KeyBundleID + transmitterID := ocrCfg.TransmitAccount + commitSpec, err := ccipJobParam.CommitJobSpec() + if err != nil { + return nil, fmt.Errorf("failed to generate commit job spec for source %s and destination %s: %w", + sourceChain.String(), destChain.String(), err) + } + commitSpec.OCR2OracleSpec.OCRKeyBundleID.SetValid(ocrKeyBundleID) + commitSpec.OCR2OracleSpec.TransmitterID.SetValid(string(transmitterID)) + commitSpecStr, err := commitSpec.String() + if err != nil { + return nil, fmt.Errorf("failed to convert commit job spec to string for source %s and destination %s: %w", + sourceChain.String(), destChain.String(), err) + } + execSpec, err := ccipJobParam.ExecutionJobSpec() + if err != nil { + return nil, fmt.Errorf("failed to generate execution job spec for source %s and destination %s: %w", + sourceChain.String(), destChain.String(), err) + } + execSpec.OCR2OracleSpec.OCRKeyBundleID.SetValid(ocrKeyBundleID) + execSpec.OCR2OracleSpec.TransmitterID.SetValid(string(transmitterID)) + execSpecStr, err := execSpec.String() + if err != nil { + return nil, fmt.Errorf("failed to convert execution job spec to string for source %s and destination %s: %w", + sourceChain.String(), destChain.String(), err) + } + specs = append(specs, commitSpecStr, execSpecStr) + } else { + bootstrapSpec := ccipJobParam.BootstrapJob(destChainState.CommitStore[cfg.SourceChainSelector].Address().String()) + bootstrapSpecStr, err := bootstrapSpec.String() + if err != nil { + return nil, fmt.Errorf("failed to convert bootstrap job spec to string for source %s and destination %s: %w", + sourceChain.String(), destChain.String(), err) + } + specs = append(specs, bootstrapSpecStr) + } + } + nodesToJobSpecs[node.NodeID] = append(nodesToJobSpecs[node.NodeID], specs...) + } + return nodesToJobSpecs, nil +} diff --git a/deployment/ccip/changeset/v1_5/cs_add_lanes.go b/deployment/ccip/changeset/v1_5/cs_lane_contracts.go similarity index 67% rename from deployment/ccip/changeset/v1_5/cs_add_lanes.go rename to deployment/ccip/changeset/v1_5/cs_lane_contracts.go index fbfe9fd3fa1..803f9cb67a7 100644 --- a/deployment/ccip/changeset/v1_5/cs_add_lanes.go +++ b/deployment/ccip/changeset/v1_5/cs_lane_contracts.go @@ -1,12 +1,9 @@ package v1_5 import ( - "context" "fmt" - "strconv" "github.com/ethereum/go-ethereum/common" - chain_selectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" @@ -15,17 +12,15 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" - integrationtesthelpers "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers/integration" ) -var _ deployment.ChangeSet[AddLanesConfig] = AddLanes +var _ deployment.ChangeSet[DeployLanesConfig] = DeployLanes -type AddLanesConfig struct { - Configs []AddLaneConfig +type DeployLanesConfig struct { + Configs []DeployLaneConfig } -func (c *AddLanesConfig) Validate(e deployment.Environment, state changeset.CCIPOnChainState) error { +func (c *DeployLanesConfig) Validate(e deployment.Environment, state changeset.CCIPOnChainState) error { for _, cfg := range c.Configs { if err := cfg.Validate(e, state); err != nil { return err @@ -34,7 +29,7 @@ func (c *AddLanesConfig) Validate(e deployment.Environment, state changeset.CCIP return nil } -type AddLaneConfig struct { +type DeployLaneConfig struct { SourceChainSelector uint64 DestinationChainSelector uint64 @@ -52,15 +47,9 @@ type AddLaneConfig struct { // Price Registry specific configuration InitialTokenPrices []price_registry_1_2_0.InternalTokenPriceUpdate GasPriceUpdates []price_registry_1_2_0.InternalGasPriceUpdate - - // Job specific configuration - TokenPricesUSDPipeline string - PriceGetterConfigJson string - USDCAttestationAPI string - USDCCfg *config.USDCConfig } -func (c *AddLaneConfig) Validate(e deployment.Environment, state changeset.CCIPOnChainState) error { +func (c *DeployLaneConfig) Validate(e deployment.Environment, state changeset.CCIPOnChainState) error { if err := deployment.IsValidChainSelector(c.SourceChainSelector); err != nil { return err } @@ -94,7 +83,7 @@ func (c *AddLaneConfig) Validate(e deployment.Environment, state changeset.CCIPO return nil } -func AddLanes(env deployment.Environment, c AddLanesConfig) (deployment.ChangesetOutput, error) { +func DeployLanes(env deployment.Environment, c DeployLanesConfig) (deployment.ChangesetOutput, error) { state, err := changeset.LoadOnchainState(env) if err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("failed to load CCIP onchain state: %w", err) @@ -103,110 +92,19 @@ func AddLanes(env deployment.Environment, c AddLanesConfig) (deployment.Changese return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig: %w", err) } newAddresses := deployment.NewMemoryAddressBook() - blocksByDest := make(map[uint64]uint64) for _, cfg := range c.Configs { - number, err := env.Chains[cfg.DestinationChainSelector].Client.HeaderByNumber(context.Background(), nil) - if err != nil { - return deployment.ChangesetOutput{ - AddressBook: newAddresses, - }, err - } - blocksByDest[cfg.DestinationChainSelector] = number.Number.Uint64() - if err := addLane(env, state, newAddresses, cfg); err != nil { + if err := deployLane(env, state, newAddresses, cfg); err != nil { return deployment.ChangesetOutput{ AddressBook: newAddresses, }, err } } - jobSpecs, err := jobSpecsForLane(env, state, c, blocksByDest) - if err != nil { - return deployment.ChangesetOutput{ - AddressBook: newAddresses, - }, err - } return deployment.ChangesetOutput{ AddressBook: newAddresses, - JobSpecs: jobSpecs, }, nil } -func jobSpecsForLane( - env deployment.Environment, - state changeset.CCIPOnChainState, - addLanesCfg AddLanesConfig, - blocksByDest map[uint64]uint64, -) (map[string][]string, error) { - nodes, err := deployment.NodeInfo(env.NodeIDs, env.Offchain) - if err != nil { - return nil, err - } - nodesToJobSpecs := make(map[string][]string) - for _, node := range nodes { - var specs []string - for _, cfg := range addLanesCfg.Configs { - var err error - destChainState := state.Chains[cfg.DestinationChainSelector] - sourceChain := env.Chains[cfg.SourceChainSelector] - destChain := env.Chains[cfg.DestinationChainSelector] - destEVMChainIdStr, err := chain_selectors.GetChainIDFromSelector(cfg.DestinationChainSelector) - if err != nil { - return nil, fmt.Errorf("failed to get chain ID from selector for chain %s: %w", destChain.String(), err) - } - destEVMChainId, err := strconv.ParseUint(destEVMChainIdStr, 10, 64) - if err != nil { - return nil, fmt.Errorf("failed to parse chain ID %s for chain %s: %w", destEVMChainIdStr, destChain.String(), err) - } - ccipJobParam := integrationtesthelpers.CCIPJobSpecParams{ - OffRamp: destChainState.EVM2EVMOffRamp[cfg.SourceChainSelector].Address(), - CommitStore: destChainState.CommitStore[cfg.SourceChainSelector].Address(), - SourceChainName: sourceChain.Name(), - DestChainName: destChain.Name(), - DestEvmChainId: destEVMChainId, - TokenPricesUSDPipeline: cfg.TokenPricesUSDPipeline, - PriceGetterConfig: cfg.PriceGetterConfigJson, - DestStartBlock: blocksByDest[cfg.DestinationChainSelector], - USDCAttestationAPI: cfg.USDCAttestationAPI, - USDCConfig: cfg.USDCCfg, - P2PV2Bootstrappers: nodes.BootstrapLocators(), - } - if !node.IsBootstrap { - commitSpec, err := ccipJobParam.CommitJobSpec() - if err != nil { - return nil, fmt.Errorf("failed to generate commit job spec for source %s and destination %s: %w", - sourceChain.String(), destChain.String(), err) - } - commitSpecStr, err := commitSpec.String() - if err != nil { - return nil, fmt.Errorf("failed to convert commit job spec to string for source %s and destination %s: %w", - sourceChain.String(), destChain.String(), err) - } - execSpec, err := ccipJobParam.ExecutionJobSpec() - if err != nil { - return nil, fmt.Errorf("failed to generate execution job spec for source %s and destination %s: %w", - sourceChain.String(), destChain.String(), err) - } - execSpecStr, err := execSpec.String() - if err != nil { - return nil, fmt.Errorf("failed to convert execution job spec to string for source %s and destination %s: %w", - sourceChain.String(), destChain.String(), err) - } - specs = append(specs, commitSpecStr, execSpecStr) - } else { - bootstrapSpec := ccipJobParam.BootstrapJob(destChainState.CommitStore[cfg.SourceChainSelector].Address().String()) - bootstrapSpecStr, err := bootstrapSpec.String() - if err != nil { - return nil, fmt.Errorf("failed to convert bootstrap job spec to string for source %s and destination %s: %w", - sourceChain.String(), destChain.String(), err) - } - specs = append(specs, bootstrapSpecStr) - } - } - nodesToJobSpecs[node.NodeID] = append(nodesToJobSpecs[node.NodeID], specs...) - } - return nodesToJobSpecs, nil -} - -func addLane(e deployment.Environment, state changeset.CCIPOnChainState, ab deployment.AddressBook, cfg AddLaneConfig) error { +func deployLane(e deployment.Environment, state changeset.CCIPOnChainState, ab deployment.AddressBook, cfg DeployLaneConfig) error { // update prices on the source price registry sourceChainState := state.Chains[cfg.SourceChainSelector] destChainState := state.Chains[cfg.DestinationChainSelector] @@ -261,7 +159,7 @@ func addLane(e deployment.Environment, state changeset.CCIPOnChainState, ab depl // Deploy commit store on source chain commitStore, commitStoreExists := destChainState.CommitStore[cfg.SourceChainSelector] if !commitStoreExists { - commitStoreC, err := deployment.DeployContract(e.Logger, sourceChain, ab, + commitStoreC, err := deployment.DeployContract(e.Logger, destChain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*commit_store.CommitStore] { commitStoreAddress, tx, commitStoreC, err2 := commit_store.DeployCommitStore( destChain.DeployerKey, @@ -292,7 +190,7 @@ func addLane(e deployment.Environment, state changeset.CCIPOnChainState, ab depl // Deploy offRamp on destination chain offRamp, offRampExists := destChainState.EVM2EVMOffRamp[cfg.SourceChainSelector] if !offRampExists { - offRampC, err := deployment.DeployContract(e.Logger, sourceChain, ab, + offRampC, err := deployment.DeployContract(e.Logger, destChain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*evm_2_evm_offramp.EVM2EVMOffRamp] { offRampAddress, tx, offRampC, err2 := evm_2_evm_offramp.DeployEVM2EVMOffRamp( destChain.DeployerKey, diff --git a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go index d7b22f26c8f..f38c9d8fa08 100644 --- a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go +++ b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go @@ -13,7 +13,6 @@ import ( "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" - ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" ) @@ -39,23 +38,21 @@ type CommitOCR2ConfigParams struct { PriceReportingDisabled bool } -func (c CommitOCR2ConfigParams) PopulateOffChainAndOnChainCfg(priceReg common.Address) error { - cfgBytes, err := ccipconfig.EncodeOffchainConfig( - testhelpers.NewCommitOffchainConfig( - c.GasPriceHeartBeat, - c.DAGasPriceDeviationPPB, - c.ExecGasPriceDeviationPPB, - c.TokenPriceHeartBeat, - c.TokenPriceDeviationPPB, - c.InflightCacheExpiry, - c.PriceReportingDisabled, - ), - ) +func (c *CommitOCR2ConfigParams) PopulateOffChainAndOnChainCfg(priceReg common.Address) error { + var err error + c.OCR2ConfigParams.ReportingPluginConfig, err = testhelpers.NewCommitOffchainConfig( + c.GasPriceHeartBeat, + c.DAGasPriceDeviationPPB, + c.ExecGasPriceDeviationPPB, + c.TokenPriceHeartBeat, + c.TokenPriceDeviationPPB, + c.InflightCacheExpiry, + c.PriceReportingDisabled, + ).Encode() if err != nil { return errors.Wrapf(err, "failed to encode offchain config for source chain %d and destination chain %d", c.SourceChainSelector, c.DestinationChainSelector) } - c.OCR2ConfigParams.ReportingPluginConfig = cfgBytes c.OCR2ConfigParams.OnchainConfig, err = abihelpers.EncodeAbiStruct(testhelpers.NewCommitOnchainConfig(priceReg)) if err != nil { return fmt.Errorf("failed to encode onchain config for source chain %d and destination chain %d: %w", @@ -64,7 +61,7 @@ func (c CommitOCR2ConfigParams) PopulateOffChainAndOnChainCfg(priceReg common.Ad return nil } -func (c CommitOCR2ConfigParams) Validate(state changeset.CCIPOnChainState) error { +func (c *CommitOCR2ConfigParams) Validate(state changeset.CCIPOnChainState) error { if err := deployment.IsValidChainSelector(c.DestinationChainSelector); err != nil { return fmt.Errorf("invalid DestinationChainSelector: %w", err) } @@ -103,7 +100,7 @@ type ExecuteOCR2ConfigParams struct { OCR2ConfigParams confighelper.PublicConfig } -func (c ExecuteOCR2ConfigParams) PopulateOffChainAndOnChainCfg(router, priceReg common.Address) error { +func (c *ExecuteOCR2ConfigParams) PopulateOffChainAndOnChainCfg(router, priceReg common.Address) error { var err error c.OCR2ConfigParams.ReportingPluginConfig, err = testhelpers.NewExecOffchainConfig( c.DestOptimisticConfirmations, @@ -131,13 +128,7 @@ func (c ExecuteOCR2ConfigParams) PopulateOffChainAndOnChainCfg(router, priceReg return nil } -func (e ExecuteOCR2ConfigParams) Validate(state changeset.CCIPOnChainState) error { - if err := e.ExecOnchainConfig.Validate(); err != nil { - return err - } - if err := e.ExecOffchainConfig.Validate(); err != nil { - return err - } +func (e *ExecuteOCR2ConfigParams) Validate(state changeset.CCIPOnChainState) error { if err := deployment.IsValidChainSelector(e.SourceChainSelector); err != nil { return fmt.Errorf("invalid SourceChainSelector: %w", err) } @@ -214,7 +205,7 @@ func SetOCR2ConfigForTest(env deployment.Environment, c OCR2Config) (deployment. ) if err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("failed to set OCR2 config for commit store %s on chain %s: %w", - commitStore.Address().String(), chain.String(), err) + commitStore.Address().String(), chain.String(), deployment.MaybeDataErr(err)) } _, err = chain.Confirm(tx) if err != nil { diff --git a/deployment/ccip/changeset/v1_5/e2e_test.go b/deployment/ccip/changeset/v1_5/e2e_test.go index 36cdbcccdb7..622fd73f4c3 100644 --- a/deployment/ccip/changeset/v1_5/e2e_test.go +++ b/deployment/ccip/changeset/v1_5/e2e_test.go @@ -1,6 +1,7 @@ package v1_5 import ( + "context" "testing" "github.com/ethereum/go-ethereum/common" @@ -32,5 +33,8 @@ func TestE2ELegacy(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, sentEvent) + destStartBlock, err := destChain.Client.HeaderByNumber(context.Background(), nil) + require.NoError(t, err) WaitForCommit(t, srcChain, destChain, state.Chains[dest].CommitStore[src], sentEvent.Message.SequenceNumber) + WaitForExecute(t, srcChain, destChain, state.Chains[dest].EVM2EVMOffRamp[src], []uint64{sentEvent.Message.SequenceNumber}, destStartBlock.Number.Uint64()) } diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index 73c8a455e2a..0c7423fbfd7 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -13,8 +13,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink-common/pkg/config" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/stretchr/testify/require" + "google.golang.org/grpc" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" @@ -27,8 +29,49 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" + ocr2validate "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" ) +type LegacyJobclient struct { + *memory.JobClient +} + +// ProposeJob is an overridden implementation of the jobclient.ProposeJob method which allows offchainreporting2 job type +func (j *LegacyJobclient) ProposeJob(ctx context.Context, in *jobv1.ProposeJobRequest, opts ...grpc.CallOption) (*jobv1.ProposeJobResponse, error) { + n := j.Nodes[in.NodeId] + // TODO: Use FMS + jb, err := ocr2validate.ValidatedOracleSpecToml( + ctx, + n.App.GetConfig().OCR2(), + n.App.GetConfig().Insecure(), + in.Spec, + nil, // not required for validation + ) + if err != nil { + jb, err = ocrbootstrap.ValidatedBootstrapSpecToml(in.Spec) + if err != nil { + return nil, fmt.Errorf("failed to validate job spec only bootstrap and offchainreporting2 are supported : %w", err) + } + } + err = n.App.AddJobV2(ctx, &jb) + if err != nil { + return nil, err + } + return &jobv1.ProposeJobResponse{Proposal: &jobv1.Proposal{ + Id: "", + // Auto approve for now + Status: jobv1.ProposalStatus_PROPOSAL_STATUS_APPROVED, + DeliveryStatus: jobv1.ProposalDeliveryStatus_PROPOSAL_DELIVERY_STATUS_DELIVERED, + Spec: in.Spec, + JobId: jb.ExternalJobID.String(), + CreatedAt: nil, + UpdatedAt: nil, + AckedAt: nil, + ResponseReceivedAt: nil, + }}, nil +} + // NewMemoryEnvironment creates an in-memory environment based on the testconfig requested func NewMemoryEnvironment(t *testing.T, opts ...changeset.TestOps) changeset.DeployedEnv { testCfg := changeset.DefaultTestConfigs() @@ -47,6 +90,9 @@ func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.Test require.NotEmpty(t, e.Env.Chains) tEnv.StartNodes(t, tc, deployment.CapabilityRegistryConfig{}) e = tEnv.DeployedEnvironment() + e.Env.Offchain = &LegacyJobclient{ + JobClient: e.Env.Offchain.(*memory.JobClient), + } allChains := e.Env.AllChainSelectors() mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) @@ -103,11 +149,11 @@ func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.Test } } } - addLanesCfg, commitOCR2Configs, execOCR2Configs := LaneConfigsForChains(t, state, pairs) + addLanesCfg, commitOCR2Configs, execOCR2Configs, jobspecs := LaneConfigsForChains(t, e.Env, state, pairs) e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, []commonchangeset.ChangesetApplication{ { - Changeset: commonchangeset.WrapChangeSet(AddLanes), - Config: AddLanesConfig{ + Changeset: commonchangeset.WrapChangeSet(DeployLanes), + Config: DeployLanesConfig{ Configs: addLanesCfg, }, }, @@ -118,19 +164,27 @@ func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.Test ExecConfigs: execOCR2Configs, }, }, + { + Changeset: commonchangeset.WrapChangeSet(JobSpecsForLanes), + Config: JobSpecsForLanesConfig{ + Configs: jobspecs, + }, + }, }) require.NoError(t, err) return e } -func LaneConfigsForChains(t *testing.T, state changeset.CCIPOnChainState, pairs []changeset.SourceDestPair) ( - []AddLaneConfig, +func LaneConfigsForChains(t *testing.T, env deployment.Environment, state changeset.CCIPOnChainState, pairs []changeset.SourceDestPair) ( + []DeployLaneConfig, []CommitOCR2ConfigParams, []ExecuteOCR2ConfigParams, + []JobSpecInput, ) { - var addLanesCfg []AddLaneConfig + var addLanesCfg []DeployLaneConfig var commitOCR2Configs []CommitOCR2ConfigParams var execOCR2Configs []ExecuteOCR2ConfigParams + var jobSpecs []JobSpecInput for _, pair := range pairs { dest := pair.DestChainSelector src := pair.SourceChainSelector @@ -146,7 +200,15 @@ func LaneConfigsForChains(t *testing.T, state changeset.CCIPOnChainState, pairs require.NotNil(t, destChainState.RMNProxy) require.NotNil(t, destChainState.TokenAdminRegistry) tokenPrice, _, _ := CreatePricesPipeline(t, state, src, dest) - addLanesCfg = append(addLanesCfg, AddLaneConfig{ + block, err := env.Chains[dest].Client.HeaderByNumber(context.Background(), nil) + require.NoError(t, err) + jobSpecs = append(jobSpecs, JobSpecInput{ + SourceChainSelector: src, + DestinationChainSelector: dest, + TokenPricesUSDPipeline: tokenPrice, + DestinationStartBlock: block.Number.Uint64(), + }) + addLanesCfg = append(addLanesCfg, DeployLaneConfig{ SourceChainSelector: src, DestinationChainSelector: dest, OnRampStaticCfg: evm_2_evm_onramp.EVM2EVMOnRampStaticConfig{ @@ -227,7 +289,6 @@ func LaneConfigsForChains(t *testing.T, state changeset.CCIPOnChainState, pairs UsdPerUnitGas: big.NewInt(20000e9), }, }, - TokenPricesUSDPipeline: tokenPrice, }) commitOCR2Configs = append(commitOCR2Configs, CommitOCR2ConfigParams{ SourceChainSelector: src, @@ -259,7 +320,7 @@ func LaneConfigsForChains(t *testing.T, state changeset.CCIPOnChainState, pairs OCR2ConfigParams: DefaultOCRParams(), }) } - return addLanesCfg, commitOCR2Configs, execOCR2Configs + return addLanesCfg, commitOCR2Configs, execOCR2Configs, jobSpecs } func CreatePricesPipeline(t *testing.T, state changeset.CCIPOnChainState, source, dest uint64) (string, *httptest.Server, *httptest.Server) { @@ -346,7 +407,7 @@ func SendRequest( } require.True(t, it.Next()) - t.Logf("CCIP message (id %x) sent from chain selector %d to chain selector %d tx %s seqNum %d nonce %d sender %s", + t.Logf("CCIP message (id %x) sent from chain selector %d to chain selector %d tx %s seqNum %d sender %s", it.Event.Message.MessageId[:], cfg.SourceChain, cfg.DestChain, diff --git a/deployment/environment.go b/deployment/environment.go index 0823404da2d..bfbeac2f0c4 100644 --- a/deployment/environment.go +++ b/deployment/environment.go @@ -74,6 +74,9 @@ func (c Chain) Name() string { // we should never get here, if the selector is invalid it should not be in the environment panic(err) } + if chainInfo.ChainName == "" { + return fmt.Sprintf("%d", c.Selector) + } return chainInfo.ChainName } diff --git a/deployment/environment/memory/environment.go b/deployment/environment/memory/environment.go index d6c80a92a44..94f9def3419 100644 --- a/deployment/environment/memory/environment.go +++ b/deployment/environment/memory/environment.go @@ -5,6 +5,7 @@ import ( "fmt" "strconv" "testing" + "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/core/types" @@ -80,10 +81,11 @@ func generateMemoryChain(t *testing.T, inputs map[uint64]EVMChain) map[uint64]de } for { backend.Commit() - receipt, err := backend.TransactionReceipt(context.Background(), tx.Hash()) - if err != nil { - t.Log("failed to get receipt", "chain", chainInfo.ChainName, err) - continue + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + receipt, err := bind.WaitMined(ctx, backend, tx) + if err != nil || receipt == nil { + return 0, fmt.Errorf("failed to get confirmed receipt for chain %s: %w", chainInfo.ChainName, err) } if receipt.Status == 0 { errReason, err := deployment.GetErrorReasonFromTx(chain.Backend.Client(), chain.DeployerKey.From, tx, receipt) diff --git a/deployment/go.mod b/deployment/go.mod index 5551034d579..8a3ddf634cf 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -364,6 +364,7 @@ require ( github.com/oklog/run v1.1.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/onsi/gomega v1.34.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect diff --git a/deployment/go.sum b/deployment/go.sum index 66170b80629..3cc2ee10451 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1225,8 +1225,8 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo= +github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= From 6bb2a6dd7203daf8a5d2276f3702cbd0bfcb7a2f Mon Sep 17 00:00:00 2001 From: AnieeG Date: Mon, 16 Dec 2024 18:40:53 -0800 Subject: [PATCH 13/29] updates --- deployment/ccip/changeset/v1_5/cs_jobspec.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/deployment/ccip/changeset/v1_5/cs_jobspec.go b/deployment/ccip/changeset/v1_5/cs_jobspec.go index 46ba50eec63..e4f926292a9 100644 --- a/deployment/ccip/changeset/v1_5/cs_jobspec.go +++ b/deployment/ccip/changeset/v1_5/cs_jobspec.go @@ -98,10 +98,6 @@ func jobSpecsForLane( if err != nil { return nil, fmt.Errorf("failed to parse chain ID %s for chain %s: %w", destEVMChainIdStr, destChain.String(), err) } - destChainDetails, err := chain_selectors.GetChainDetailsByChainIDAndFamily(destEVMChainIdStr, chain_selectors.FamilyEVM) - if err != nil { - return nil, fmt.Errorf("failed to get chain details for chain ID %s: %w", destEVMChainIdStr, err) - } ccipJobParam := integrationtesthelpers.CCIPJobSpecParams{ OffRamp: destChainState.EVM2EVMOffRamp[cfg.SourceChainSelector].Address(), CommitStore: destChainState.CommitStore[cfg.SourceChainSelector].Address(), @@ -116,7 +112,10 @@ func jobSpecsForLane( P2PV2Bootstrappers: nodes.BootstrapLocators(), } if !node.IsBootstrap { - ocrCfg := node.SelToOCRConfig[destChainDetails] + ocrCfg, found := node.OCRConfigForChainSelector(cfg.DestinationChainSelector) + if !found { + return nil, fmt.Errorf("OCR config not found for chain %s", destChain.String()) + } ocrKeyBundleID := ocrCfg.KeyBundleID transmitterID := ocrCfg.TransmitAccount commitSpec, err := ccipJobParam.CommitJobSpec() From 8d2ea2a4e4d175cd1c62ea334ea58eaead049ff5 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 17 Dec 2024 14:13:28 -0800 Subject: [PATCH 14/29] changes --- deployment/ccip/changeset/test_environment.go | 12 ++-- deployment/ccip/changeset/v1_5/cs_jobspec.go | 16 +---- .../ccip/changeset/v1_5/cs_ocr2_config.go | 5 +- deployment/ccip/changeset/v1_5/e2e_test.go | 17 ++++- .../ccip/changeset/v1_5/test_helpers.go | 69 ++++++++++++++----- .../smoke/ccip/ccip_legacy_test.go | 42 +++++++++++ .../testsetups/ccip/test_helpers.go | 24 +++++++ 7 files changed, 144 insertions(+), 41 deletions(-) create mode 100644 integration-tests/smoke/ccip/ccip_legacy_test.go diff --git a/deployment/ccip/changeset/test_environment.go b/deployment/ccip/changeset/test_environment.go index a7d8a923da5..cb1b3110005 100644 --- a/deployment/ccip/changeset/test_environment.go +++ b/deployment/ccip/changeset/test_environment.go @@ -200,7 +200,7 @@ func (d *DeployedEnv) SetupJobs(t *testing.T) { type MemoryEnvironment struct { DeployedEnv - chains map[uint64]deployment.Chain + Chains map[uint64]deployment.Chain } func (m *MemoryEnvironment) DeployedEnvironment() DeployedEnv { @@ -210,13 +210,13 @@ func (m *MemoryEnvironment) DeployedEnvironment() DeployedEnv { func (m *MemoryEnvironment) StartChains(t *testing.T, tc *TestConfigs) { ctx := testcontext.Get(t) chains, users := memory.NewMemoryChains(t, tc.Chains, tc.NumOfUsersPerChain) - m.chains = chains + m.Chains = chains homeChainSel, feedSel := allocateCCIPChainSelectors(chains) replayBlocks, err := LatestBlocksByChain(ctx, chains) require.NoError(t, err) m.DeployedEnv = DeployedEnv{ Env: deployment.Environment{ - Chains: m.chains, + Chains: m.Chains, }, HomeChainSel: homeChainSel, FeedChainSel: feedSel, @@ -226,9 +226,9 @@ func (m *MemoryEnvironment) StartChains(t *testing.T, tc *TestConfigs) { } func (m *MemoryEnvironment) StartNodes(t *testing.T, tc *TestConfigs, crConfig deployment.CapabilityRegistryConfig) { - require.NotNil(t, m.chains, "start chains first, chains are empty") + require.NotNil(t, m.Chains, "start chains first, chains are empty") require.NotNil(t, m.DeployedEnv, "start chains and initiate deployed env first before starting nodes") - nodes := memory.NewNodes(t, zapcore.InfoLevel, m.chains, tc.Nodes, tc.Bootstraps, crConfig) + nodes := memory.NewNodes(t, zapcore.InfoLevel, m.Chains, tc.Nodes, tc.Bootstraps, crConfig) ctx := testcontext.Get(t) lggr := logger.Test(t) for _, node := range nodes { @@ -237,7 +237,7 @@ func (m *MemoryEnvironment) StartNodes(t *testing.T, tc *TestConfigs, crConfig d require.NoError(t, node.App.Stop()) }) } - m.DeployedEnv.Env = memory.NewMemoryEnvironmentFromChainsNodes(func() context.Context { return ctx }, lggr, m.chains, nodes) + m.DeployedEnv.Env = memory.NewMemoryEnvironmentFromChainsNodes(func() context.Context { return ctx }, lggr, m.Chains, nodes) } func (m *MemoryEnvironment) MockUSDCAttestationServer(t *testing.T, isUSDCAttestationMissing bool) string { diff --git a/deployment/ccip/changeset/v1_5/cs_jobspec.go b/deployment/ccip/changeset/v1_5/cs_jobspec.go index e4f926292a9..bdb36d531f8 100644 --- a/deployment/ccip/changeset/v1_5/cs_jobspec.go +++ b/deployment/ccip/changeset/v1_5/cs_jobspec.go @@ -2,9 +2,6 @@ package v1_5 import ( "fmt" - "strconv" - - chain_selectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" @@ -28,6 +25,7 @@ func (c JobSpecsForLanesConfig) Validate() error { type JobSpecInput struct { SourceChainSelector uint64 DestinationChainSelector uint64 + DestEVMChainID uint64 DestinationStartBlock uint64 TokenPricesUSDPipeline string PriceGetterConfigJson string @@ -86,24 +84,16 @@ func jobSpecsForLane( for _, node := range nodes { var specs []string for _, cfg := range lanesCfg.Configs { - var err error destChainState := state.Chains[cfg.DestinationChainSelector] sourceChain := env.Chains[cfg.SourceChainSelector] destChain := env.Chains[cfg.DestinationChainSelector] - destEVMChainIdStr, err := chain_selectors.GetChainIDFromSelector(cfg.DestinationChainSelector) - if err != nil { - return nil, fmt.Errorf("failed to get chain ID from selector for chain %s: %w", destChain.String(), err) - } - destEVMChainId, err := strconv.ParseUint(destEVMChainIdStr, 10, 64) - if err != nil { - return nil, fmt.Errorf("failed to parse chain ID %s for chain %s: %w", destEVMChainIdStr, destChain.String(), err) - } + ccipJobParam := integrationtesthelpers.CCIPJobSpecParams{ OffRamp: destChainState.EVM2EVMOffRamp[cfg.SourceChainSelector].Address(), CommitStore: destChainState.CommitStore[cfg.SourceChainSelector].Address(), SourceChainName: sourceChain.Name(), DestChainName: destChain.Name(), - DestEvmChainId: destEVMChainId, + DestEvmChainId: cfg.DestEVMChainID, TokenPricesUSDPipeline: cfg.TokenPricesUSDPipeline, PriceGetterConfig: cfg.PriceGetterConfigJson, DestStartBlock: cfg.DestinationStartBlock, diff --git a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go index f38c9d8fa08..29fa5c404e0 100644 --- a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go +++ b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go @@ -271,8 +271,9 @@ func deriveOCR2Config( OnchainPublicKey: cfg.OnchainPublicKey, TransmitAccount: cfg.TransmitAccount, OffchainPublicKey: cfg.OffchainPublicKey, - PeerID: cfg.PeerID.String()[4:], - }, ConfigEncryptionPublicKey: cfg.ConfigEncryptionPublicKey, + PeerID: cfg.PeerID.Raw(), + }, + ConfigEncryptionPublicKey: cfg.ConfigEncryptionPublicKey, }) } diff --git a/deployment/ccip/changeset/v1_5/e2e_test.go b/deployment/ccip/changeset/v1_5/e2e_test.go index 622fd73f4c3..c7eadf08000 100644 --- a/deployment/ccip/changeset/v1_5/e2e_test.go +++ b/deployment/ccip/changeset/v1_5/e2e_test.go @@ -5,20 +5,31 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + chainselectors "github.com/smartcontractkit/chain-selectors" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" ) +// This test only works if the destination chain id is 1337 func TestE2ELegacy(t *testing.T) { - e := NewMemoryEnvironment(t) + e := NewMemoryEnvironment(t, changeset.WithChains(3)) state, err := changeset.LoadOnchainState(e.Env) require.NoError(t, err) - allChains := e.Env.AllChainSelectors() - src, dest := allChains[0], allChains[1] + allChains := e.Env.AllChainSelectorsExcluding([]uint64{chainselectors.GETH_TESTNET.Selector}) + require.Contains(t, e.Env.AllChainSelectors(), chainselectors.GETH_TESTNET.Selector) + require.Len(t, allChains, 2) + src, dest := allChains[1], chainselectors.GETH_TESTNET.Selector srcChain := e.Env.Chains[src] destChain := e.Env.Chains[dest] + pairs := []changeset.SourceDestPair{ + {SourceChainSelector: src, DestChainSelector: dest}, + } + e.Env = AddLanes(t, e.Env, state, pairs) + // reload state after adding lanes + state, err = changeset.LoadOnchainState(e.Env) + require.NoError(t, err) sentEvent, err := SendRequest(t, e.Env, state, changeset.WithSourceChain(src), changeset.WithDestChain(dest), diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index 0c7423fbfd7..f57cac0a161 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -6,14 +6,17 @@ import ( "math/big" "net/http" "net/http/httptest" + "strconv" "testing" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + chain_selectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/config" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" + "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/stretchr/testify/require" "google.golang.org/grpc" @@ -72,14 +75,45 @@ func (j *LegacyJobclient) ProposeJob(ctx context.Context, in *jobv1.ProposeJobRe }}, nil } +type MemoryEnvironment struct { + changeset.MemoryEnvironment +} + +// StartChains is to override the default changeset.MemoryEnvironment.StartChains method +// to ensure we always have a chain with chain id 1337 +// this is required for the offchain config digest to be consistent with onchain config digest +// off chain config digests are calculated based on the chain id which needs to be always 1337 for simulated backend +// chains, otherwise it will not match with the onchain config digest +func (m *MemoryEnvironment) StartChains(t *testing.T, tc *changeset.TestConfigs) { + ctx := testcontext.Get(t) + chains, users := memory.NewMemoryChains(t, tc.Chains-1, tc.NumOfUsersPerChain) + // Add chain with chain id 1337 + chain1337 := memory.NewMemoryChainsWithChainIDs(t, []uint64{1337}) + for k, v := range chain1337 { + chains[k] = v + } + replayBlocks, err := changeset.LatestBlocksByChain(ctx, chains) + require.NoError(t, err) + m.Chains = chains + m.DeployedEnv = changeset.DeployedEnv{ + Env: deployment.Environment{ + Chains: chains, + }, + ReplayBlocks: replayBlocks, + Users: users, + } +} + // NewMemoryEnvironment creates an in-memory environment based on the testconfig requested +// This environment currently only works when destination chain is 1337 +// Otherwise it shows error for offchain and onchain config digest mismatch func NewMemoryEnvironment(t *testing.T, opts ...changeset.TestOps) changeset.DeployedEnv { testCfg := changeset.DefaultTestConfigs() for _, opt := range opts { opt(testCfg) } require.NoError(t, testCfg.Validate(), "invalid test config") - env := &changeset.MemoryEnvironment{} + env := &MemoryEnvironment{} return NewEnvironment(t, testCfg, env) } @@ -90,8 +124,10 @@ func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.Test require.NotEmpty(t, e.Env.Chains) tEnv.StartNodes(t, tc, deployment.CapabilityRegistryConfig{}) e = tEnv.DeployedEnvironment() - e.Env.Offchain = &LegacyJobclient{ - JobClient: e.Env.Offchain.(*memory.JobClient), + if _, ok := e.Env.Offchain.(*memory.JobClient); ok { + e.Env.Offchain = &LegacyJobclient{ + JobClient: e.Env.Offchain.(*memory.JobClient), + } } allChains := e.Env.AllChainSelectors() @@ -136,21 +172,15 @@ func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.Test }, }) require.NoError(t, err) - state, err := changeset.LoadOnchainState(e.Env) + return e +} + +func AddLanes(t *testing.T, e deployment.Environment, state changeset.CCIPOnChainState, pairs []changeset.SourceDestPair) deployment.Environment { + state, err := changeset.LoadOnchainState(e) require.NoError(t, err) - var pairs []changeset.SourceDestPair - for _, src := range allChains { - for _, dest := range allChains { - if src != dest { - pairs = append(pairs, changeset.SourceDestPair{ - SourceChainSelector: src, - DestChainSelector: dest, - }) - } - } - } - addLanesCfg, commitOCR2Configs, execOCR2Configs, jobspecs := LaneConfigsForChains(t, e.Env, state, pairs) - e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, []commonchangeset.ChangesetApplication{ + + addLanesCfg, commitOCR2Configs, execOCR2Configs, jobspecs := LaneConfigsForChains(t, e, state, pairs) + e, err = commonchangeset.ApplyChangesets(t, e, nil, []commonchangeset.ChangesetApplication{ { Changeset: commonchangeset.WrapChangeSet(DeployLanes), Config: DeployLanesConfig{ @@ -202,9 +232,14 @@ func LaneConfigsForChains(t *testing.T, env deployment.Environment, state change tokenPrice, _, _ := CreatePricesPipeline(t, state, src, dest) block, err := env.Chains[dest].Client.HeaderByNumber(context.Background(), nil) require.NoError(t, err) + destEVMChainIdStr, err := chain_selectors.GetChainIDFromSelector(dest) + require.NoError(t, err) + destEVMChainId, err := strconv.ParseUint(destEVMChainIdStr, 10, 64) + require.NoError(t, err) jobSpecs = append(jobSpecs, JobSpecInput{ SourceChainSelector: src, DestinationChainSelector: dest, + DestEVMChainID: destEVMChainId, TokenPricesUSDPipeline: tokenPrice, DestinationStartBlock: block.Number.Uint64(), }) diff --git a/integration-tests/smoke/ccip/ccip_legacy_test.go b/integration-tests/smoke/ccip/ccip_legacy_test.go new file mode 100644 index 00000000000..5c6220b2c96 --- /dev/null +++ b/integration-tests/smoke/ccip/ccip_legacy_test.go @@ -0,0 +1,42 @@ +package smoke + +import ( + "context" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_5" + testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" +) + +func TestE2ELegacy(t *testing.T) { + e := testsetups.NewIntegrationLegacyEnvironment(t) + state, err := changeset.LoadOnchainState(e.Env) + require.NoError(t, err) + allChains := e.Env.AllChainSelectors() + src, dest := allChains[0], allChains[1] + srcChain := e.Env.Chains[src] + destChain := e.Env.Chains[dest] + sentEvent, err := v1_5.SendRequest(t, e.Env, state, + changeset.WithSourceChain(src), + changeset.WithDestChain(dest), + changeset.WithTestRouter(false), + changeset.WithEvm2AnyMessage(router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32), + Data: []byte("hello"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + }), + ) + require.NoError(t, err) + require.NotNil(t, sentEvent) + destStartBlock, err := destChain.Client.HeaderByNumber(context.Background(), nil) + require.NoError(t, err) + v1_5.WaitForCommit(t, srcChain, destChain, state.Chains[dest].CommitStore[src], sentEvent.Message.SequenceNumber) + v1_5.WaitForExecute(t, srcChain, destChain, state.Chains[dest].EVM2EVMOffRamp[src], []uint64{sentEvent.Message.SequenceNumber}, destStartBlock.Number.Uint64()) +} diff --git a/integration-tests/testsetups/ccip/test_helpers.go b/integration-tests/testsetups/ccip/test_helpers.go index 56c6e7310ba..d1aaca94027 100644 --- a/integration-tests/testsetups/ccip/test_helpers.go +++ b/integration-tests/testsetups/ccip/test_helpers.go @@ -27,6 +27,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_5" integrationnodes "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" corechainlink "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -137,6 +138,29 @@ func (l *DeployedLocalDevEnvironment) RestartChainlinkNodes(t *testing.T) error return errGrp.Wait() } +func NewIntegrationLegacyEnvironment(t *testing.T, opts ...changeset.TestOps) changeset.DeployedEnv { + testCfg := changeset.DefaultTestConfigs() + for _, opt := range opts { + opt(testCfg) + } + // check for EnvType env var + testCfg.MustSetEnvTypeOrDefault(t) + require.NoError(t, testCfg.Validate(), "invalid test config") + switch testCfg.Type { + case changeset.Memory: + memEnv := v1_5.NewMemoryEnvironment(t, opts...) + return memEnv + case changeset.Docker: + dockerEnv := &DeployedLocalDevEnvironment{} + deployedEnv := v1_5.NewEnvironment(t, testCfg, dockerEnv) + require.NotNil(t, dockerEnv.testEnv, "empty docker environment") + return deployedEnv + default: + require.Failf(t, "Type %s not supported in integration tests choose between %s and %s", string(testCfg.Type), changeset.Memory, changeset.Docker) + } + return changeset.DeployedEnv{} +} + // NewIntegrationEnvironment creates a new integration test environment based on the provided test config // It can create a memory environment or a docker environment based on env var CCIP_V16_TEST_ENV // By default, it creates a memory environment if env var CCIP_V16_TEST_ENV is not set From 58615329e566c169ae6acf719cfbca16c0f81e6e Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 17 Dec 2024 14:48:58 -0800 Subject: [PATCH 15/29] updates --- .../ccip/testhelpers/integration/jobspec.go | 25 +++++++++++++++++++ .../ccip/changeset/cs_prerequisites_test.go | 6 ++++- .../changeset/cs_update_rmn_config_test.go | 6 ++++- .../ccip/changeset/v1_5/test_helpers.go | 4 +++ .../smoke/ccip/ccip_legacy_test.go | 9 +++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go index b10f51a9426..9c263b9e128 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go @@ -2,11 +2,13 @@ package integrationtesthelpers import ( "bytes" + "crypto/sha256" "fmt" "text/template" "time" "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" "github.com/lib/pq" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -28,6 +30,7 @@ type OCR2TaskJobSpec struct { ForwardingAllowed bool `toml:"forwardingAllowed"` OCR2OracleSpec job.OCR2OracleSpec ObservationSource string `toml:"observationSource"` // List of commands for the Chainlink node + ExternalJobID string `toml:"externalJobID"` } // Type returns the type of the job @@ -39,9 +42,14 @@ func (o *OCR2TaskJobSpec) String() (string, error) { if o.OCR2OracleSpec.FeedID != nil { feedID = o.OCR2OracleSpec.FeedID.Hex() } + externalId, err := ExternalJobID(o.Name) + if err != nil { + return "", err + } specWrap := struct { Name string JobType string + ExternalJobID string MaxTaskDuration string ForwardingAllowed bool ContractID string @@ -62,6 +70,7 @@ func (o *OCR2TaskJobSpec) String() (string, error) { }{ Name: o.Name, JobType: o.JobType, + ExternalJobID: externalId, ForwardingAllowed: o.ForwardingAllowed, MaxTaskDuration: o.MaxTaskDuration, ContractID: o.OCR2OracleSpec.ContractID, @@ -82,6 +91,7 @@ func (o *OCR2TaskJobSpec) String() (string, error) { ocr2TemplateString := ` type = "{{ .JobType }}" name = "{{.Name}}" +externalJobID = "{{.ExternalJobID}}" forwardingAllowed = {{.ForwardingAllowed}} {{if .MaxTaskDuration}} maxTaskDuration = "{{ .MaxTaskDuration }}" {{end}} @@ -332,3 +342,18 @@ func (c *CCIPIntegrationTestHarness) NewCCIPJobSpecParams(tokenPricesUSDPipeline USDCAttestationAPI: usdcAttestationAPI, } } + +func ExternalJobID(jobName string) (string, error) { + in := []byte(jobName) + sha256Hash := sha256.New() + sha256Hash.Write(in) + in = sha256Hash.Sum(nil)[:16] + // tag as valid UUID v4 https://github.com/google/uuid/blob/0f11ee6918f41a04c201eceeadf612a377bc7fbc/version4.go#L53-L54 + in[6] = (in[6] & 0x0f) | 0x40 // Version 4 + in[8] = (in[8] & 0x3f) | 0x80 // Variant is 10 + id, err := uuid.FromBytes(in) + if err != nil { + return "", err + } + return id.String(), nil +} diff --git a/deployment/ccip/changeset/cs_prerequisites_test.go b/deployment/ccip/changeset/cs_prerequisites_test.go index da1ff9c83a9..5835bd41aa3 100644 --- a/deployment/ccip/changeset/cs_prerequisites_test.go +++ b/deployment/ccip/changeset/cs_prerequisites_test.go @@ -20,7 +20,11 @@ func TestDeployPrerequisites(t *testing.T) { }) newChain := e.AllChainSelectors()[0] cfg := DeployPrerequisiteConfig{ - ChainSelectors: []uint64{newChain}, + Configs: []DeployPrerequisiteConfigPerChain{ + { + ChainSelector: newChain, + }, + }, } output, err := DeployPrerequisites(e, cfg) require.NoError(t, err) diff --git a/deployment/ccip/changeset/cs_update_rmn_config_test.go b/deployment/ccip/changeset/cs_update_rmn_config_test.go index 07bf22720c2..e7543e22cb7 100644 --- a/deployment/ccip/changeset/cs_update_rmn_config_test.go +++ b/deployment/ccip/changeset/cs_update_rmn_config_test.go @@ -224,8 +224,12 @@ func TestSetRMNRemoteOnRMNProxy(t *testing.T) { allChains := e.Env.AllChainSelectors() mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) var err error + var prereqCfgs []DeployPrerequisiteConfigPerChain for _, c := range e.Env.AllChainSelectors() { mcmsCfg[c] = proposalutils.SingleGroupTimelockConfig(t) + prereqCfgs = append(prereqCfgs, DeployPrerequisiteConfigPerChain{ + ChainSelector: c, + }) } // Need to deploy prerequisites first so that we can form the USDC config // no proposals to be made, timelock can be passed as nil here @@ -237,7 +241,7 @@ func TestSetRMNRemoteOnRMNProxy(t *testing.T) { { Changeset: commonchangeset.WrapChangeSet(DeployPrerequisites), Config: DeployPrerequisiteConfig{ - ChainSelectors: allChains, + Configs: prereqCfgs, }, }, { diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index f57cac0a161..24a7c1ae9cc 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -508,6 +508,7 @@ func WaitForExecute( if backend, ok := dest.Client.(*memory.Backend); ok { backend.Commit() } + t.Logf("Waiting for execute for sequence numbers %v", seqNrs) it, err := offRamp.FilterExecutionStateChanged( &bind.FilterOpts{ Start: blockNum, @@ -518,6 +519,9 @@ func WaitForExecute( if cciptypes.MessageExecutionState(it.Event.State) == cciptypes.ExecutionStateSuccess { t.Logf("Execution for sequence number %d found", it.Event.SequenceNumber) return + } else { + t.Logf("Execution for sequence number %d failed status %d", it.Event.SequenceNumber, it.Event.State) + t.Fail() } } case <-timer.C: diff --git a/integration-tests/smoke/ccip/ccip_legacy_test.go b/integration-tests/smoke/ccip/ccip_legacy_test.go index 5c6220b2c96..b18957dbeee 100644 --- a/integration-tests/smoke/ccip/ccip_legacy_test.go +++ b/integration-tests/smoke/ccip/ccip_legacy_test.go @@ -13,14 +13,23 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" ) +// This test does not run in CI, it is only written as an example of how to write a test for the legacy CCIP func TestE2ELegacy(t *testing.T) { e := testsetups.NewIntegrationLegacyEnvironment(t) state, err := changeset.LoadOnchainState(e.Env) require.NoError(t, err) allChains := e.Env.AllChainSelectors() + require.Len(t, allChains, 2) src, dest := allChains[0], allChains[1] srcChain := e.Env.Chains[src] destChain := e.Env.Chains[dest] + pairs := []changeset.SourceDestPair{ + {SourceChainSelector: src, DestChainSelector: dest}, + } + e.Env = v1_5.AddLanes(t, e.Env, state, pairs) + // reload state after adding lanes + state, err = changeset.LoadOnchainState(e.Env) + require.NoError(t, err) sentEvent, err := v1_5.SendRequest(t, e.Env, state, changeset.WithSourceChain(src), changeset.WithDestChain(dest), From 572fe53f7c7e00aaf5a92ac179f0e8f7accf73b8 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 17 Dec 2024 14:50:26 -0800 Subject: [PATCH 16/29] formatting --- .../ocr2/plugins/ccip/testhelpers/integration/jobspec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go index 9c263b9e128..1e0c44866fe 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go @@ -91,7 +91,7 @@ func (o *OCR2TaskJobSpec) String() (string, error) { ocr2TemplateString := ` type = "{{ .JobType }}" name = "{{.Name}}" -externalJobID = "{{.ExternalJobID}}" +externalJobID = "{{.ExternalJobID}}" forwardingAllowed = {{.ForwardingAllowed}} {{if .MaxTaskDuration}} maxTaskDuration = "{{ .MaxTaskDuration }}" {{end}} From 37ac6e9ba7f37df8b05aa8a81e2525bc2a4565ac Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 17 Dec 2024 15:01:21 -0800 Subject: [PATCH 17/29] fix crib --- deployment/environment/crib/ccip_deployer.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/deployment/environment/crib/ccip_deployer.go b/deployment/environment/crib/ccip_deployer.go index aea7ad0cb8f..4c52cc72416 100644 --- a/deployment/environment/crib/ccip_deployer.go +++ b/deployment/environment/crib/ccip_deployer.go @@ -4,13 +4,15 @@ import ( "context" "errors" "fmt" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/environment/devenv" "github.com/smartcontractkit/chainlink/v2/core/services/relay" - "math/big" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" @@ -71,8 +73,9 @@ func DeployCCIPAndAddLanes(ctx context.Context, lggr logger.Logger, envConfig de return DeployCCIPOutput{}, fmt.Errorf("failed to initiate new environment: %w", err) } e.ExistingAddresses = ab - allChainIds := e.AllChainSelectors() + chainSelectors := e.AllChainSelectors() cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) + var prereqCfgs []changeset.DeployPrerequisiteConfigPerChain for _, chain := range e.AllChainSelectors() { mcmsConfig, err := config.NewConfig(1, []common.Address{e.Chains[chain].DeployerKey.From}, []config.Config{}) if err != nil { @@ -84,6 +87,9 @@ func DeployCCIPAndAddLanes(ctx context.Context, lggr logger.Logger, envConfig de Proposer: *mcmsConfig, TimelockMinDelay: big.NewInt(0), } + prereqCfgs = append(prereqCfgs, changeset.DeployPrerequisiteConfigPerChain{ + ChainSelector: chain, + }) } // This will not apply any proposals because we pass nil to testing. @@ -91,12 +97,12 @@ func DeployCCIPAndAddLanes(ctx context.Context, lggr logger.Logger, envConfig de *e, err = commonchangeset.ApplyChangesets(nil, *e, nil, []commonchangeset.ChangesetApplication{ { Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployLinkToken), - Config: allChainIds, + Config: chainSelectors, }, { Changeset: commonchangeset.WrapChangeSet(changeset.DeployPrerequisites), Config: changeset.DeployPrerequisiteConfig{ - ChainSelectors: allChainIds, + Configs: prereqCfgs, }, }, { @@ -106,7 +112,7 @@ func DeployCCIPAndAddLanes(ctx context.Context, lggr logger.Logger, envConfig de { Changeset: commonchangeset.WrapChangeSet(changeset.DeployChainContracts), Config: changeset.DeployChainContractsConfig{ - ChainSelectors: allChainIds, + ChainSelectors: chainSelectors, HomeChainSelector: homeChainSel, }, }, From edd1ef1a6de085c4c1ee70167745125339c16ebd Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 17 Dec 2024 15:11:37 -0800 Subject: [PATCH 18/29] fix more tests --- deployment/ccip/changeset/state.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deployment/ccip/changeset/state.go b/deployment/ccip/changeset/state.go index 2490f621e35..a4c7d987efb 100644 --- a/deployment/ccip/changeset/state.go +++ b/deployment/ccip/changeset/state.go @@ -356,7 +356,11 @@ func (s CCIPOnChainState) View(chains []uint64) (map[string]view.ChainView, erro if err != nil { return m, err } - m[chainInfo.ChainName] = chainView + name := chainInfo.ChainName + if chainInfo.ChainName == "" { + name = fmt.Sprintf("%d", chainSelector) + } + m[name] = chainView } return m, nil } From ea8197297295bae629b886e94bd6a5a302581c38 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Tue, 17 Dec 2024 17:06:29 -0800 Subject: [PATCH 19/29] lint fix --- .../ocr2/plugins/ccip/testhelpers/integration/jobspec.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go index 1e0c44866fe..a520580e614 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go @@ -42,7 +42,7 @@ func (o *OCR2TaskJobSpec) String() (string, error) { if o.OCR2OracleSpec.FeedID != nil { feedID = o.OCR2OracleSpec.FeedID.Hex() } - externalId, err := ExternalJobID(o.Name) + externalID, err := ExternalJobID(o.Name) if err != nil { return "", err } @@ -70,7 +70,7 @@ func (o *OCR2TaskJobSpec) String() (string, error) { }{ Name: o.Name, JobType: o.JobType, - ExternalJobID: externalId, + ExternalJobID: externalID, ForwardingAllowed: o.ForwardingAllowed, MaxTaskDuration: o.MaxTaskDuration, ContractID: o.OCR2OracleSpec.ContractID, From 599bb5c464e15395e40bd7ef053a578a31e27cd8 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 18 Dec 2024 08:10:02 -0800 Subject: [PATCH 20/29] comments --- deployment/environment/memory/environment.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/deployment/environment/memory/environment.go b/deployment/environment/memory/environment.go index 94f9def3419..e6496f52492 100644 --- a/deployment/environment/memory/environment.go +++ b/deployment/environment/memory/environment.go @@ -81,11 +81,13 @@ func generateMemoryChain(t *testing.T, inputs map[uint64]EVMChain) map[uint64]de } for { backend.Commit() - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - receipt, err := bind.WaitMined(ctx, backend, tx) - if err != nil || receipt == nil { - return 0, fmt.Errorf("failed to get confirmed receipt for chain %s: %w", chainInfo.ChainName, err) + receipt, err := func() (*types.Receipt, error) { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + return bind.WaitMined(ctx, backend, tx) + }() + if err != nil { + return 0, fmt.Errorf("tx %s failed to confirm: %v, chain %s", tx.Hash().Hex(), err, chainInfo.ChainSelector) } if receipt.Status == 0 { errReason, err := deployment.GetErrorReasonFromTx(chain.Backend.Client(), chain.DeployerKey.From, tx, receipt) From 4669024d392e4ab2710cd77e4b85627b2a225226 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 18 Dec 2024 08:30:38 -0800 Subject: [PATCH 21/29] fix format --- deployment/environment/memory/environment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/environment/memory/environment.go b/deployment/environment/memory/environment.go index e6496f52492..7ba37fd689f 100644 --- a/deployment/environment/memory/environment.go +++ b/deployment/environment/memory/environment.go @@ -87,7 +87,7 @@ func generateMemoryChain(t *testing.T, inputs map[uint64]EVMChain) map[uint64]de return bind.WaitMined(ctx, backend, tx) }() if err != nil { - return 0, fmt.Errorf("tx %s failed to confirm: %v, chain %s", tx.Hash().Hex(), err, chainInfo.ChainSelector) + return 0, fmt.Errorf("tx %s failed to confirm: %v, chain %d", tx.Hash().Hex(), err, chainInfo.ChainSelector) } if receipt.Status == 0 { errReason, err := deployment.GetErrorReasonFromTx(chain.Backend.Client(), chain.DeployerKey.From, tx, receipt) From b76b6fccb8abf3e168a893350b27b449973cb717 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 18 Dec 2024 10:37:56 -0800 Subject: [PATCH 22/29] multichain --- deployment/multiclient.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/deployment/multiclient.go b/deployment/multiclient.go index f1ac2f3c310..9765e0368ea 100644 --- a/deployment/multiclient.go +++ b/deployment/multiclient.go @@ -109,16 +109,16 @@ func (mc *MultiClient) NonceAt(ctx context.Context, account common.Address, bloc } func (mc *MultiClient) WaitMined(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) { - mc.lggr.Debugf("Waiting for tx %s to be mined", tx.Hash().Hex()) + mc.lggr.Debugf("Waiting for tx %s to be mined for chain %s", tx.Hash().Hex(), mc.chainName) // no retries here because we want to wait for the tx to be mined resultCh := make(chan *types.Receipt) doneCh := make(chan struct{}) waitMined := func(client *ethclient.Client, tx *types.Transaction) { - mc.lggr.Debugf("Waiting for tx %s to be mined with client %v", tx.Hash().Hex(), client) + mc.lggr.Debugf("Waiting for tx %s to be mined with chain %s", tx.Hash().Hex(), mc.chainName) receipt, err := bind.WaitMined(ctx, client, tx) if err != nil { - mc.lggr.Warnf("WaitMined error %v with client %v", err, client) + mc.lggr.Warnf("WaitMined error %v with chain %s", err, mc.chainName) return } select { @@ -135,6 +135,7 @@ func (mc *MultiClient) WaitMined(ctx context.Context, tx *types.Transaction) (*t select { case receipt = <-resultCh: close(doneCh) + mc.lggr.Debugf("Tx %s mined with chain %s", tx.Hash().Hex(), mc.chainName) return receipt, nil case <-ctx.Done(): mc.lggr.Warnf("WaitMined context done %v", ctx.Err()) From 88301f3ff943cc90c9a233a7f7042fc620a2c02d Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 18 Dec 2024 10:54:35 -0800 Subject: [PATCH 23/29] fix lint --- .../ccip/changeset/v1_5/cs_ocr2_config.go | 28 +++++++++---------- .../ccip/changeset/v1_5/test_helpers.go | 5 ++-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go index 29fa5c404e0..2569c186633 100644 --- a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go +++ b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go @@ -100,30 +100,30 @@ type ExecuteOCR2ConfigParams struct { OCR2ConfigParams confighelper.PublicConfig } -func (c *ExecuteOCR2ConfigParams) PopulateOffChainAndOnChainCfg(router, priceReg common.Address) error { +func (e *ExecuteOCR2ConfigParams) PopulateOffChainAndOnChainCfg(router, priceReg common.Address) error { var err error - c.OCR2ConfigParams.ReportingPluginConfig, err = testhelpers.NewExecOffchainConfig( - c.DestOptimisticConfirmations, - c.BatchGasLimit, - c.RelativeBoostPerWaitHour, - c.InflightCacheExpiry, - c.RootSnoozeTime, - c.BatchingStrategyID, + e.OCR2ConfigParams.ReportingPluginConfig, err = testhelpers.NewExecOffchainConfig( + e.DestOptimisticConfirmations, + e.BatchGasLimit, + e.RelativeBoostPerWaitHour, + e.InflightCacheExpiry, + e.RootSnoozeTime, + e.BatchingStrategyID, ).Encode() if err != nil { return fmt.Errorf("failed to encode offchain config for exec plugin, source chain %d dest chain %d :%w", - c.SourceChainSelector, c.DestinationChainSelector, err) + e.SourceChainSelector, e.DestinationChainSelector, err) } - c.OCR2ConfigParams.OnchainConfig, err = abihelpers.EncodeAbiStruct(testhelpers.NewExecOnchainConfig( - c.ExecOnchainConfig.PermissionLessExecutionThresholdSeconds, + e.OCR2ConfigParams.OnchainConfig, err = abihelpers.EncodeAbiStruct(testhelpers.NewExecOnchainConfig( + e.ExecOnchainConfig.PermissionLessExecutionThresholdSeconds, router, priceReg, - c.ExecOnchainConfig.MaxNumberOfTokensPerMsg, - c.ExecOnchainConfig.MaxDataBytes, + e.ExecOnchainConfig.MaxNumberOfTokensPerMsg, + e.ExecOnchainConfig.MaxDataBytes, )) if err != nil { return fmt.Errorf("failed to encode onchain config for exec plugin, source chain %d dest chain %d :%w", - c.SourceChainSelector, c.DestinationChainSelector, err) + e.SourceChainSelector, e.DestinationChainSelector, err) } return nil } diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index 24a7c1ae9cc..a311b14809b 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -519,10 +519,9 @@ func WaitForExecute( if cciptypes.MessageExecutionState(it.Event.State) == cciptypes.ExecutionStateSuccess { t.Logf("Execution for sequence number %d found", it.Event.SequenceNumber) return - } else { - t.Logf("Execution for sequence number %d failed status %d", it.Event.SequenceNumber, it.Event.State) - t.Fail() } + t.Logf("Execution for sequence number %d resulted in status %d", it.Event.SequenceNumber, it.Event.State) + t.Fail() } case <-timer.C: t.Fatalf("timed out waiting for execute for sequence numbers %v for offramp %s ", seqNrs, offRamp.Address().String()) From 347a77512166b0d0da4d9452e58b65bbee4b8877 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 18 Dec 2024 11:59:49 -0800 Subject: [PATCH 24/29] fix lint --- deployment/ccip/changeset/cs_prerequisites.go | 16 ++++++++-------- .../ccip/changeset/v1_5/cs_lane_contracts.go | 12 ++++++------ deployment/ccip/changeset/v1_5/cs_ocr2_config.go | 3 +++ deployment/ccip/changeset/v1_5/test_helpers.go | 4 +--- deployment/environment/memory/environment.go | 2 +- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/deployment/ccip/changeset/cs_prerequisites.go b/deployment/ccip/changeset/cs_prerequisites.go index b811be3be47..fa70bf62a47 100644 --- a/deployment/ccip/changeset/cs_prerequisites.go +++ b/deployment/ccip/changeset/cs_prerequisites.go @@ -188,7 +188,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address chain.Client, ) return deployment.ContractDeploy[*mock_rmn_contract.MockRMNContract]{ - rmnAddress, rmnC, tx2, deployment.NewTypeAndVersion(MockRMN, deployment.Version1_0_0), err2, + Address: rmnAddress, Contract: rmnC, Tx: tx2, Tv: deployment.NewTypeAndVersion(MockRMN, deployment.Version1_0_0), Err: err2, } }) if err != nil { @@ -210,7 +210,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address rmnAddr, ) return deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract]{ - rmnProxyAddr, rmnProxy, tx2, deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_0_0), err2, + Address: rmnProxyAddr, Contract: rmnProxy, Tx: tx2, Tv: deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_0_0), Err: err2, } }) if err != nil { @@ -259,7 +259,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address chain.DeployerKey, chain.Client) return deployment.ContractDeploy[*token_admin_registry.TokenAdminRegistry]{ - tokenAdminRegistryAddr, tokenAdminRegistry, tx2, deployment.NewTypeAndVersion(TokenAdminRegistry, deployment.Version1_5_0), err2, + Address: tokenAdminRegistryAddr, Contract: tokenAdminRegistry, Tx: tx2, Tv: deployment.NewTypeAndVersion(TokenAdminRegistry, deployment.Version1_5_0), Err: err2, } }) if err != nil { @@ -278,7 +278,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address chain.Client, tokenAdminReg.Address()) return deployment.ContractDeploy[*registry_module_owner_custom.RegistryModuleOwnerCustom]{ - regModAddr, regMod, tx2, deployment.NewTypeAndVersion(RegistryModule, deployment.Version1_5_0), err2, + Address: regModAddr, Contract: regMod, Tx: tx2, Tv: deployment.NewTypeAndVersion(RegistryModule, deployment.Version1_5_0), Err: err2, } }) if err != nil { @@ -316,7 +316,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address chain.Client, ) return deployment.ContractDeploy[*weth9.WETH9]{ - weth9Addr, weth9c, tx2, deployment.NewTypeAndVersion(WETH9, deployment.Version1_0_0), err2, + Address: weth9Addr, Contract: weth9c, Tx: tx2, Tv: deployment.NewTypeAndVersion(WETH9, deployment.Version1_0_0), Err: err2, } }) if err != nil { @@ -340,7 +340,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address rmnProxy.Address(), ) return deployment.ContractDeploy[*router.Router]{ - routerAddr, routerC, tx2, deployment.NewTypeAndVersion(Router, deployment.Version1_2_0), err2, + Address: routerAddr, Contract: routerC, Tx: tx2, Tv: deployment.NewTypeAndVersion(Router, deployment.Version1_2_0), Err: err2, } }) if err != nil { @@ -360,7 +360,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address chain.Client, ) return deployment.ContractDeploy[*multicall3.Multicall3]{ - multicall3Addr, multicall3Wrapper, tx2, deployment.NewTypeAndVersion(Multicall3, deployment.Version1_0_0), err2, + Address: multicall3Addr, Contract: multicall3Wrapper, Tx: tx2, Tv: deployment.NewTypeAndVersion(Multicall3, deployment.Version1_0_0), Err: err2, } }) if err != nil { @@ -394,7 +394,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address false, ) return deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver]{ - receiverAddr, receiver, tx, deployment.NewTypeAndVersion(CCIPReceiver, deployment.Version1_0_0), err2, + Address: receiverAddr, Contract: receiver, Tx: tx, Tv: deployment.NewTypeAndVersion(CCIPReceiver, deployment.Version1_0_0), Err: err2, } }) if err != nil { diff --git a/deployment/ccip/changeset/v1_5/cs_lane_contracts.go b/deployment/ccip/changeset/v1_5/cs_lane_contracts.go index 803f9cb67a7..2d6c8fcb5ed 100644 --- a/deployment/ccip/changeset/v1_5/cs_lane_contracts.go +++ b/deployment/ccip/changeset/v1_5/cs_lane_contracts.go @@ -130,7 +130,7 @@ func deployLane(e deployment.Environment, state changeset.CCIPOnChainState, ab d if !onRampExists { onRampC, err := deployment.DeployContract(e.Logger, sourceChain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*evm_2_evm_onramp.EVM2EVMOnRamp] { - onRampAddress, tx, onRampC, err2 := evm_2_evm_onramp.DeployEVM2EVMOnRamp( + onRampAddress, tx2, onRampC, err2 := evm_2_evm_onramp.DeployEVM2EVMOnRamp( sourceChain.DeployerKey, sourceChain.Client, cfg.OnRampStaticCfg, @@ -141,7 +141,7 @@ func deployLane(e deployment.Environment, state changeset.CCIPOnChainState, ab d cfg.OnRampNopsAndWeight, ) return deployment.ContractDeploy[*evm_2_evm_onramp.EVM2EVMOnRamp]{ - Address: onRampAddress, Contract: onRampC, Tx: tx, + Address: onRampAddress, Contract: onRampC, Tx: tx2, Tv: deployment.NewTypeAndVersion(changeset.OnRamp, deployment.Version1_5_0), Err: err2, } }) @@ -161,7 +161,7 @@ func deployLane(e deployment.Environment, state changeset.CCIPOnChainState, ab d if !commitStoreExists { commitStoreC, err := deployment.DeployContract(e.Logger, destChain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*commit_store.CommitStore] { - commitStoreAddress, tx, commitStoreC, err2 := commit_store.DeployCommitStore( + commitStoreAddress, tx2, commitStoreC, err2 := commit_store.DeployCommitStore( destChain.DeployerKey, destChain.Client, commit_store.CommitStoreStaticConfig{ @@ -172,7 +172,7 @@ func deployLane(e deployment.Environment, state changeset.CCIPOnChainState, ab d }, ) return deployment.ContractDeploy[*commit_store.CommitStore]{ - Address: commitStoreAddress, Contract: commitStoreC, Tx: tx, + Address: commitStoreAddress, Contract: commitStoreC, Tx: tx2, Tv: deployment.NewTypeAndVersion(changeset.CommitStore, deployment.Version1_5_0), Err: err2, } }) @@ -192,7 +192,7 @@ func deployLane(e deployment.Environment, state changeset.CCIPOnChainState, ab d if !offRampExists { offRampC, err := deployment.DeployContract(e.Logger, destChain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*evm_2_evm_offramp.EVM2EVMOffRamp] { - offRampAddress, tx, offRampC, err2 := evm_2_evm_offramp.DeployEVM2EVMOffRamp( + offRampAddress, tx2, offRampC, err2 := evm_2_evm_offramp.DeployEVM2EVMOffRamp( destChain.DeployerKey, destChain.Client, evm_2_evm_offramp.EVM2EVMOffRampStaticConfig{ @@ -207,7 +207,7 @@ func deployLane(e deployment.Environment, state changeset.CCIPOnChainState, ab d cfg.OffRampRateLimiterCfg, ) return deployment.ContractDeploy[*evm_2_evm_offramp.EVM2EVMOffRamp]{ - Address: offRampAddress, Contract: offRampC, Tx: tx, + Address: offRampAddress, Contract: offRampC, Tx: tx2, Tv: deployment.NewTypeAndVersion(changeset.OffRamp, deployment.Version1_5_0), Err: err2, } }) diff --git a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go index 2569c186633..f4127b6894c 100644 --- a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go +++ b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go @@ -296,6 +296,9 @@ func deriveOCR2Config( int(nodes.DefaultF()), ocrParams.OnchainConfig, ) + if err != nil { + return FinalOCR2Config{}, fmt.Errorf("failed to derive OCR2 config: %w", err) + } var signersAddresses []common.Address for _, signer := range signers { if len(signer) != 20 { diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index a311b14809b..36c89e2a6c0 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -176,10 +176,8 @@ func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.Test } func AddLanes(t *testing.T, e deployment.Environment, state changeset.CCIPOnChainState, pairs []changeset.SourceDestPair) deployment.Environment { - state, err := changeset.LoadOnchainState(e) - require.NoError(t, err) - addLanesCfg, commitOCR2Configs, execOCR2Configs, jobspecs := LaneConfigsForChains(t, e, state, pairs) + var err error e, err = commonchangeset.ApplyChangesets(t, e, nil, []commonchangeset.ChangesetApplication{ { Changeset: commonchangeset.WrapChangeSet(DeployLanes), diff --git a/deployment/environment/memory/environment.go b/deployment/environment/memory/environment.go index 7ba37fd689f..b90d97f44bf 100644 --- a/deployment/environment/memory/environment.go +++ b/deployment/environment/memory/environment.go @@ -87,7 +87,7 @@ func generateMemoryChain(t *testing.T, inputs map[uint64]EVMChain) map[uint64]de return bind.WaitMined(ctx, backend, tx) }() if err != nil { - return 0, fmt.Errorf("tx %s failed to confirm: %v, chain %d", tx.Hash().Hex(), err, chainInfo.ChainSelector) + return 0, fmt.Errorf("tx %s failed to confirm: %w, chain %d", tx.Hash().Hex(), err, chainInfo.ChainSelector) } if receipt.Status == 0 { errReason, err := deployment.GetErrorReasonFromTx(chain.Backend.Client(), chain.DeployerKey.From, tx, receipt) From 207a6bc7adcc3751c7fb8fd88e1b31f8db3df7a1 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 18 Dec 2024 12:19:21 -0800 Subject: [PATCH 25/29] change func name --- deployment/ccip/changeset/cs_prerequisites.go | 2 +- deployment/ccip/changeset/test_environment.go | 3 ++- deployment/ccip/changeset/v1_5/test_helpers.go | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/deployment/ccip/changeset/cs_prerequisites.go b/deployment/ccip/changeset/cs_prerequisites.go index fa70bf62a47..1d0339b48c7 100644 --- a/deployment/ccip/changeset/cs_prerequisites.go +++ b/deployment/ccip/changeset/cs_prerequisites.go @@ -92,7 +92,7 @@ func WithUSDCEnabled() PrerequisiteOpt { } } -func WithMulticall3() PrerequisiteOpt { +func WithMultiCall3Enabled() PrerequisiteOpt { return func(o *DeployPrerequisiteContractsOpts) { o.Multicall3Enabled = true } diff --git a/deployment/ccip/changeset/test_environment.go b/deployment/ccip/changeset/test_environment.go index 79b3a94d5fb..393aa770077 100644 --- a/deployment/ccip/changeset/test_environment.go +++ b/deployment/ccip/changeset/test_environment.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" @@ -330,7 +331,7 @@ func NewEnvironmentWithJobsAndContracts(t *testing.T, tc *TestConfigs, tEnv Test opts = append(opts, WithUSDCEnabled()) } if tc.IsMultiCall3 { - opts = append(opts, WithMulticall3()) + opts = append(opts, WithMultiCall3Enabled()) } } prereqCfg = append(prereqCfg, DeployPrerequisiteConfigPerChain{ diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index 36c89e2a6c0..11efe679ab2 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -143,7 +143,7 @@ func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.Test opts = append(opts, changeset.WithUSDCEnabled()) } if tc.IsMultiCall3 { - opts = append(opts, changeset.WithMulticall3()) + opts = append(opts, changeset.WithMultiCall3Enabled()) } } opts = append(opts, changeset.WithLegacyDeployment(changeset.LegacyDeploymentConfig{ From d64279ba042c4809bf7ae17637ff7d2b9e41bf23 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 18 Dec 2024 15:12:16 -0800 Subject: [PATCH 26/29] review comments --- deployment/ccip/changeset/state.go | 4 +-- deployment/ccip/changeset/test_environment.go | 32 ++++++++++++++++--- deployment/ccip/changeset/v1_5/e2e_test.go | 2 +- .../ccip/changeset/v1_5/test_helpers.go | 32 +------------------ .../common/changeset/internal/mcms_test.go | 8 ++--- deployment/environment/memory/chain.go | 5 +-- deployment/environment/memory/environment.go | 12 +++++-- 7 files changed, 47 insertions(+), 48 deletions(-) diff --git a/deployment/ccip/changeset/state.go b/deployment/ccip/changeset/state.go index 8f1836912db..b96279d8956 100644 --- a/deployment/ccip/changeset/state.go +++ b/deployment/ccip/changeset/state.go @@ -252,10 +252,10 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) { } // Legacy contracts if c.CommitStore != nil { - for _, commitStore := range c.CommitStore { + for source, commitStore := range c.CommitStore { commitStoreView, err := v1_5.GenerateCommitStoreView(commitStore) if err != nil { - return chainView, errors.Wrapf(err, "failed to generate commit store view for commit store %s", commitStore.Address().String()) + return chainView, errors.Wrapf(err, "failed to generate commit store view for commit store %s for source %d", commitStore.Address().String(), source) } chainView.CommitStore[commitStore.Address().Hex()] = commitStoreView } diff --git a/deployment/ccip/changeset/test_environment.go b/deployment/ccip/changeset/test_environment.go index 393aa770077..813e3509ee0 100644 --- a/deployment/ccip/changeset/test_environment.go +++ b/deployment/ccip/changeset/test_environment.go @@ -43,10 +43,11 @@ type TestConfigs struct { CreateJob bool // TODO: This should be CreateContracts so the booleans make sense? CreateJobAndContracts bool - Chains int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input - NumOfUsersPerChain int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input - Nodes int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input - Bootstraps int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input + Chains int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input + ChainIDs []uint64 // only used in memory mode, for docker mode, this is determined by the integration-test config toml input + NumOfUsersPerChain int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input + Nodes int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input + Bootstraps int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input IsUSDC bool IsUSDCAttestationMissing bool IsMultiCall3 bool @@ -104,6 +105,12 @@ func WithMultiCall3() TestOps { } } +func WithChainIds(chainIDs []uint64) TestOps { + return func(testCfg *TestConfigs) { + testCfg.ChainIDs = chainIDs + } +} + func WithJobsOnly() TestOps { return func(testCfg *TestConfigs) { testCfg.CreateJobAndContracts = false @@ -228,7 +235,22 @@ func (m *MemoryEnvironment) DeployedEnvironment() DeployedEnv { func (m *MemoryEnvironment) StartChains(t *testing.T, tc *TestConfigs) { ctx := testcontext.Get(t) - chains, users := memory.NewMemoryChains(t, tc.Chains, tc.NumOfUsersPerChain) + var chains map[uint64]deployment.Chain + var users map[uint64][]*bind.TransactOpts + if len(tc.ChainIDs) > 0 { + chains, users = memory.NewMemoryChainsWithChainIDs(t, tc.ChainIDs, tc.NumOfUsersPerChain) + if tc.Chains > len(tc.ChainIDs) { + additionalChains, additionalUsers := memory.NewMemoryChains(t, tc.Chains-len(tc.ChainIDs), tc.NumOfUsersPerChain) + for k, v := range additionalChains { + chains[k] = v + } + for k, v := range additionalUsers { + users[k] = v + } + } + } else { + chains, users = memory.NewMemoryChains(t, tc.Chains, tc.NumOfUsersPerChain) + } m.Chains = chains homeChainSel, feedSel := allocateCCIPChainSelectors(chains) replayBlocks, err := LatestBlocksByChain(ctx, chains) diff --git a/deployment/ccip/changeset/v1_5/e2e_test.go b/deployment/ccip/changeset/v1_5/e2e_test.go index c7eadf08000..4a12fb4d03f 100644 --- a/deployment/ccip/changeset/v1_5/e2e_test.go +++ b/deployment/ccip/changeset/v1_5/e2e_test.go @@ -14,7 +14,7 @@ import ( // This test only works if the destination chain id is 1337 func TestE2ELegacy(t *testing.T) { - e := NewMemoryEnvironment(t, changeset.WithChains(3)) + e := NewMemoryEnvironment(t, changeset.WithChains(3), changeset.WithChainIds([]uint64{chainselectors.GETH_TESTNET.EvmChainID})) state, err := changeset.LoadOnchainState(e.Env) require.NoError(t, err) allChains := e.Env.AllChainSelectorsExcluding([]uint64{chainselectors.GETH_TESTNET.Selector}) diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index 11efe679ab2..826fe8bb77d 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -16,7 +16,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/config" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/stretchr/testify/require" "google.golang.org/grpc" @@ -75,35 +74,6 @@ func (j *LegacyJobclient) ProposeJob(ctx context.Context, in *jobv1.ProposeJobRe }}, nil } -type MemoryEnvironment struct { - changeset.MemoryEnvironment -} - -// StartChains is to override the default changeset.MemoryEnvironment.StartChains method -// to ensure we always have a chain with chain id 1337 -// this is required for the offchain config digest to be consistent with onchain config digest -// off chain config digests are calculated based on the chain id which needs to be always 1337 for simulated backend -// chains, otherwise it will not match with the onchain config digest -func (m *MemoryEnvironment) StartChains(t *testing.T, tc *changeset.TestConfigs) { - ctx := testcontext.Get(t) - chains, users := memory.NewMemoryChains(t, tc.Chains-1, tc.NumOfUsersPerChain) - // Add chain with chain id 1337 - chain1337 := memory.NewMemoryChainsWithChainIDs(t, []uint64{1337}) - for k, v := range chain1337 { - chains[k] = v - } - replayBlocks, err := changeset.LatestBlocksByChain(ctx, chains) - require.NoError(t, err) - m.Chains = chains - m.DeployedEnv = changeset.DeployedEnv{ - Env: deployment.Environment{ - Chains: chains, - }, - ReplayBlocks: replayBlocks, - Users: users, - } -} - // NewMemoryEnvironment creates an in-memory environment based on the testconfig requested // This environment currently only works when destination chain is 1337 // Otherwise it shows error for offchain and onchain config digest mismatch @@ -113,7 +83,7 @@ func NewMemoryEnvironment(t *testing.T, opts ...changeset.TestOps) changeset.Dep opt(testCfg) } require.NoError(t, testCfg.Validate(), "invalid test config") - env := &MemoryEnvironment{} + env := &changeset.MemoryEnvironment{} return NewEnvironment(t, testCfg, env) } diff --git a/deployment/common/changeset/internal/mcms_test.go b/deployment/common/changeset/internal/mcms_test.go index ff013717d30..92822422daa 100644 --- a/deployment/common/changeset/internal/mcms_test.go +++ b/deployment/common/changeset/internal/mcms_test.go @@ -18,9 +18,9 @@ import ( func TestDeployMCMSWithConfig(t *testing.T) { lggr := logger.TestLogger(t) - chains := memory.NewMemoryChainsWithChainIDs(t, []uint64{ + chains, _ := memory.NewMemoryChainsWithChainIDs(t, []uint64{ chainsel.TEST_90000001.EvmChainID, - }) + }, 1) ab := deployment.NewMemoryAddressBook() _, err := internal.DeployMCMSWithConfig(types.ProposerManyChainMultisig, lggr, chains[chainsel.TEST_90000001.Selector], ab, proposalutils.SingleGroupMCMS(t)) @@ -29,9 +29,9 @@ func TestDeployMCMSWithConfig(t *testing.T) { func TestDeployMCMSWithTimelockContracts(t *testing.T) { lggr := logger.TestLogger(t) - chains := memory.NewMemoryChainsWithChainIDs(t, []uint64{ + chains, _ := memory.NewMemoryChainsWithChainIDs(t, []uint64{ chainsel.TEST_90000001.EvmChainID, - }) + }, 1) ab := deployment.NewMemoryAddressBook() _, err := internal.DeployMCMSWithTimelockContracts(lggr, chains[chainsel.TEST_90000001.Selector], diff --git a/deployment/environment/memory/chain.go b/deployment/environment/memory/chain.go index 40a20a02416..77a8f397d39 100644 --- a/deployment/environment/memory/chain.go +++ b/deployment/environment/memory/chain.go @@ -14,6 +14,7 @@ import ( chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" ) @@ -52,10 +53,10 @@ func GenerateChains(t *testing.T, numChains int, numUsers int) map[uint64]EVMCha return chains } -func GenerateChainsWithIds(t *testing.T, chainIDs []uint64) map[uint64]EVMChain { +func GenerateChainsWithIds(t *testing.T, chainIDs []uint64, numUsers int) map[uint64]EVMChain { chains := make(map[uint64]EVMChain) for _, chainID := range chainIDs { - chains[chainID] = evmChain(t, 1) + chains[chainID] = evmChain(t, numUsers) } return chains } diff --git a/deployment/environment/memory/environment.go b/deployment/environment/memory/environment.go index b90d97f44bf..a74d23a847b 100644 --- a/deployment/environment/memory/environment.go +++ b/deployment/environment/memory/environment.go @@ -59,9 +59,15 @@ func NewMemoryChains(t *testing.T, numChains int, numUsers int) (map[uint64]depl return generateMemoryChain(t, mchains), users } -func NewMemoryChainsWithChainIDs(t *testing.T, chainIDs []uint64) map[uint64]deployment.Chain { - mchains := GenerateChainsWithIds(t, chainIDs) - return generateMemoryChain(t, mchains) +func NewMemoryChainsWithChainIDs(t *testing.T, chainIDs []uint64, numUsers int) (map[uint64]deployment.Chain, map[uint64][]*bind.TransactOpts) { + mchains := GenerateChainsWithIds(t, chainIDs, numUsers) + users := make(map[uint64][]*bind.TransactOpts) + for id, chain := range mchains { + sel, err := chainsel.SelectorFromChainId(id) + require.NoError(t, err) + users[sel] = chain.Users + } + return generateMemoryChain(t, mchains), users } func generateMemoryChain(t *testing.T, inputs map[uint64]EVMChain) map[uint64]deployment.Chain { From 08d138cc8d493123208bfdbee5d9a366b04c84f4 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 18 Dec 2024 15:20:45 -0800 Subject: [PATCH 27/29] lint fix --- deployment/ccip/changeset/v1_5/cs_ocr2_config.go | 3 ++- deployment/ccip/changeset/v1_5/test_helpers.go | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go index f4127b6894c..497bcb53ad8 100644 --- a/deployment/ccip/changeset/v1_5/cs_ocr2_config.go +++ b/deployment/ccip/changeset/v1_5/cs_ocr2_config.go @@ -6,9 +6,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" + "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index 826fe8bb77d..c50fd5bf38c 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -13,13 +13,14 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" chain_selectors "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/chainlink-common/pkg/config" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" - jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/stretchr/testify/require" "google.golang.org/grpc" + "github.com/smartcontractkit/chainlink-common/pkg/config" + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" From 514aefc4c79f20d178d6b0091190303905d5f95f Mon Sep 17 00:00:00 2001 From: AnieeG Date: Wed, 18 Dec 2024 15:32:41 -0800 Subject: [PATCH 28/29] removed legacyjobclient --- .../ccip/changeset/v1_5/test_helpers.go | 53 +------------------ deployment/environment/memory/job_client.go | 24 ++++++++- 2 files changed, 25 insertions(+), 52 deletions(-) diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index c50fd5bf38c..ff123cc40a7 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -13,13 +13,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" chain_selectors "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "github.com/smartcontractkit/chainlink-common/pkg/config" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" - jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" + "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" + "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" @@ -32,49 +29,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_2_0" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" - ocr2validate "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" - "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" ) -type LegacyJobclient struct { - *memory.JobClient -} - -// ProposeJob is an overridden implementation of the jobclient.ProposeJob method which allows offchainreporting2 job type -func (j *LegacyJobclient) ProposeJob(ctx context.Context, in *jobv1.ProposeJobRequest, opts ...grpc.CallOption) (*jobv1.ProposeJobResponse, error) { - n := j.Nodes[in.NodeId] - // TODO: Use FMS - jb, err := ocr2validate.ValidatedOracleSpecToml( - ctx, - n.App.GetConfig().OCR2(), - n.App.GetConfig().Insecure(), - in.Spec, - nil, // not required for validation - ) - if err != nil { - jb, err = ocrbootstrap.ValidatedBootstrapSpecToml(in.Spec) - if err != nil { - return nil, fmt.Errorf("failed to validate job spec only bootstrap and offchainreporting2 are supported : %w", err) - } - } - err = n.App.AddJobV2(ctx, &jb) - if err != nil { - return nil, err - } - return &jobv1.ProposeJobResponse{Proposal: &jobv1.Proposal{ - Id: "", - // Auto approve for now - Status: jobv1.ProposalStatus_PROPOSAL_STATUS_APPROVED, - DeliveryStatus: jobv1.ProposalDeliveryStatus_PROPOSAL_DELIVERY_STATUS_DELIVERED, - Spec: in.Spec, - JobId: jb.ExternalJobID.String(), - CreatedAt: nil, - UpdatedAt: nil, - AckedAt: nil, - ResponseReceivedAt: nil, - }}, nil -} - // NewMemoryEnvironment creates an in-memory environment based on the testconfig requested // This environment currently only works when destination chain is 1337 // Otherwise it shows error for offchain and onchain config digest mismatch @@ -95,11 +51,6 @@ func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.Test require.NotEmpty(t, e.Env.Chains) tEnv.StartNodes(t, tc, deployment.CapabilityRegistryConfig{}) e = tEnv.DeployedEnvironment() - if _, ok := e.Env.Offchain.(*memory.JobClient); ok { - e.Env.Offchain = &LegacyJobclient{ - JobClient: e.Env.Offchain.(*memory.JobClient), - } - } allChains := e.Env.AllChainSelectors() mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) diff --git a/deployment/environment/memory/job_client.go b/deployment/environment/memory/job_client.go index 98fb90ceffa..a3cfee41608 100644 --- a/deployment/environment/memory/job_client.go +++ b/deployment/environment/memory/job_client.go @@ -20,6 +20,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/validate" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" + ocr2validate "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" ) type JobClient struct { @@ -295,7 +297,27 @@ func (j JobClient) ProposeJob(ctx context.Context, in *jobv1.ProposeJobRequest, // TODO: Use FMS jb, err := validate.ValidatedCCIPSpec(in.Spec) if err != nil { - return nil, err + if !strings.Contains(err.Error(), "the only supported type is currently 'ccip'") { + return nil, err + } + // check if it's offchainreporting2 job + jb, err = ocr2validate.ValidatedOracleSpecToml( + ctx, + n.App.GetConfig().OCR2(), + n.App.GetConfig().Insecure(), + in.Spec, + nil, // not required for validation + ) + if err != nil { + if !strings.Contains(err.Error(), "the only supported type is currently 'offchainreporting2'") { + return nil, err + } + // check if it's bootstrap job + jb, err = ocrbootstrap.ValidatedBootstrapSpecToml(in.Spec) + if err != nil { + return nil, fmt.Errorf("failed to validate job spec only ccip, bootstrap and offchainreporting2 are supported: %w", err) + } + } } err = n.App.AddJobV2(ctx, &jb) if err != nil { From cd17fe1ee11207c43de5d26ccff436ef2bf0c1a4 Mon Sep 17 00:00:00 2001 From: AnieeG Date: Thu, 19 Dec 2024 08:38:07 -0800 Subject: [PATCH 29/29] review comments --- deployment/ccip/changeset/cs_prerequisites.go | 2 +- deployment/ccip/changeset/test_environment.go | 63 ++++++++++++++++ deployment/ccip/changeset/v1_5/e2e_test.go | 7 +- .../ccip/changeset/v1_5/test_helpers.go | 73 +------------------ .../smoke/ccip/ccip_legacy_test.go | 2 +- .../testsetups/ccip/test_helpers.go | 29 ++------ 6 files changed, 79 insertions(+), 97 deletions(-) diff --git a/deployment/ccip/changeset/cs_prerequisites.go b/deployment/ccip/changeset/cs_prerequisites.go index 1d0339b48c7..94535df4a0f 100644 --- a/deployment/ccip/changeset/cs_prerequisites.go +++ b/deployment/ccip/changeset/cs_prerequisites.go @@ -98,7 +98,7 @@ func WithMultiCall3Enabled() PrerequisiteOpt { } } -func WithLegacyDeployment(cfg LegacyDeploymentConfig) PrerequisiteOpt { +func WithLegacyDeploymentEnabled(cfg LegacyDeploymentConfig) PrerequisiteOpt { return func(o *DeployPrerequisiteContractsOpts) { if cfg.PriceRegStalenessThreshold == 0 { panic("PriceRegStalenessThreshold must be set") diff --git a/deployment/ccip/changeset/test_environment.go b/deployment/ccip/changeset/test_environment.go index 813e3509ee0..f9ec4bed242 100644 --- a/deployment/ccip/changeset/test_environment.go +++ b/deployment/ccip/changeset/test_environment.go @@ -43,6 +43,7 @@ type TestConfigs struct { CreateJob bool // TODO: This should be CreateContracts so the booleans make sense? CreateJobAndContracts bool + LegacyDeployment bool Chains int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input ChainIDs []uint64 // only used in memory mode, for docker mode, this is determined by the integration-test config toml input NumOfUsersPerChain int // only used in memory mode, for docker mode, this is determined by the integration-test config toml input @@ -105,6 +106,12 @@ func WithMultiCall3() TestOps { } } +func WithLegacyDeployment() TestOps { + return func(testCfg *TestConfigs) { + testCfg.LegacyDeployment = true + } +} + func WithChainIds(chainIDs []uint64) TestOps { return func(testCfg *TestConfigs) { testCfg.ChainIDs = chainIDs @@ -298,6 +305,9 @@ func NewMemoryEnvironment(t *testing.T, opts ...TestOps) DeployedEnv { } require.NoError(t, testCfg.Validate(), "invalid test config") env := &MemoryEnvironment{} + if testCfg.LegacyDeployment { + return NewLegacyEnvironment(t, testCfg, env) + } if testCfg.CreateJobAndContracts { return NewEnvironmentWithJobsAndContracts(t, testCfg, env) } @@ -307,6 +317,59 @@ func NewMemoryEnvironment(t *testing.T, opts ...TestOps) DeployedEnv { return NewEnvironment(t, testCfg, env) } +func NewLegacyEnvironment(t *testing.T, tc *TestConfigs, tEnv TestEnvironment) DeployedEnv { + var err error + tEnv.StartChains(t, tc) + e := tEnv.DeployedEnvironment() + require.NotEmpty(t, e.Env.Chains) + tEnv.StartNodes(t, tc, deployment.CapabilityRegistryConfig{}) + e = tEnv.DeployedEnvironment() + allChains := e.Env.AllChainSelectors() + + mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) + for _, c := range e.Env.AllChainSelectors() { + mcmsCfg[c] = proposalutils.SingleGroupTimelockConfig(t) + } + var prereqCfg []DeployPrerequisiteConfigPerChain + for _, chain := range allChains { + var opts []PrerequisiteOpt + if tc != nil { + if tc.IsUSDC { + opts = append(opts, WithUSDCEnabled()) + } + if tc.IsMultiCall3 { + opts = append(opts, WithMultiCall3Enabled()) + } + } + opts = append(opts, WithLegacyDeploymentEnabled(LegacyDeploymentConfig{ + PriceRegStalenessThreshold: 60 * 60 * 24 * 14, // two weeks + })) + prereqCfg = append(prereqCfg, DeployPrerequisiteConfigPerChain{ + ChainSelector: chain, + Opts: opts, + }) + } + + e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, []commonchangeset.ChangesetApplication{ + { + Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployLinkToken), + Config: allChains, + }, + { + Changeset: commonchangeset.WrapChangeSet(DeployPrerequisites), + Config: DeployPrerequisiteConfig{ + Configs: prereqCfg, + }, + }, + { + Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployMCMSWithTimelock), + Config: mcmsCfg, + }, + }) + require.NoError(t, err) + return e +} + func NewEnvironment(t *testing.T, tc *TestConfigs, tEnv TestEnvironment) DeployedEnv { lggr := logger.Test(t) tEnv.StartChains(t, tc) diff --git a/deployment/ccip/changeset/v1_5/e2e_test.go b/deployment/ccip/changeset/v1_5/e2e_test.go index 4a12fb4d03f..11bb566c641 100644 --- a/deployment/ccip/changeset/v1_5/e2e_test.go +++ b/deployment/ccip/changeset/v1_5/e2e_test.go @@ -13,8 +13,13 @@ import ( ) // This test only works if the destination chain id is 1337 +// Otherwise it shows error for offchain and onchain config digest mismatch func TestE2ELegacy(t *testing.T) { - e := NewMemoryEnvironment(t, changeset.WithChains(3), changeset.WithChainIds([]uint64{chainselectors.GETH_TESTNET.EvmChainID})) + e := changeset.NewMemoryEnvironment( + t, + changeset.WithLegacyDeployment(), + changeset.WithChains(3), + changeset.WithChainIds([]uint64{chainselectors.GETH_TESTNET.EvmChainID})) state, err := changeset.LoadOnchainState(e.Env) require.NoError(t, err) allChains := e.Env.AllChainSelectorsExcluding([]uint64{chainselectors.GETH_TESTNET.Selector}) diff --git a/deployment/ccip/changeset/v1_5/test_helpers.go b/deployment/ccip/changeset/v1_5/test_helpers.go index ff123cc40a7..e1a03539a77 100644 --- a/deployment/ccip/changeset/v1_5/test_helpers.go +++ b/deployment/ccip/changeset/v1_5/test_helpers.go @@ -13,16 +13,15 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" chain_selectors "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/chainlink-common/pkg/config" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/config" + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" - "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" - commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" @@ -31,72 +30,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" ) -// NewMemoryEnvironment creates an in-memory environment based on the testconfig requested -// This environment currently only works when destination chain is 1337 -// Otherwise it shows error for offchain and onchain config digest mismatch -func NewMemoryEnvironment(t *testing.T, opts ...changeset.TestOps) changeset.DeployedEnv { - testCfg := changeset.DefaultTestConfigs() - for _, opt := range opts { - opt(testCfg) - } - require.NoError(t, testCfg.Validate(), "invalid test config") - env := &changeset.MemoryEnvironment{} - return NewEnvironment(t, testCfg, env) -} - -func NewEnvironment(t *testing.T, tc *changeset.TestConfigs, tEnv changeset.TestEnvironment) changeset.DeployedEnv { - var err error - tEnv.StartChains(t, tc) - e := tEnv.DeployedEnvironment() - require.NotEmpty(t, e.Env.Chains) - tEnv.StartNodes(t, tc, deployment.CapabilityRegistryConfig{}) - e = tEnv.DeployedEnvironment() - allChains := e.Env.AllChainSelectors() - - mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig) - for _, c := range e.Env.AllChainSelectors() { - mcmsCfg[c] = proposalutils.SingleGroupTimelockConfig(t) - } - var prereqCfg []changeset.DeployPrerequisiteConfigPerChain - for _, chain := range allChains { - var opts []changeset.PrerequisiteOpt - if tc != nil { - if tc.IsUSDC { - opts = append(opts, changeset.WithUSDCEnabled()) - } - if tc.IsMultiCall3 { - opts = append(opts, changeset.WithMultiCall3Enabled()) - } - } - opts = append(opts, changeset.WithLegacyDeployment(changeset.LegacyDeploymentConfig{ - PriceRegStalenessThreshold: 60 * 60 * 24 * 14, // two weeks - })) - prereqCfg = append(prereqCfg, changeset.DeployPrerequisiteConfigPerChain{ - ChainSelector: chain, - Opts: opts, - }) - } - - e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, []commonchangeset.ChangesetApplication{ - { - Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployLinkToken), - Config: allChains, - }, - { - Changeset: commonchangeset.WrapChangeSet(changeset.DeployPrerequisites), - Config: changeset.DeployPrerequisiteConfig{ - Configs: prereqCfg, - }, - }, - { - Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployMCMSWithTimelock), - Config: mcmsCfg, - }, - }) - require.NoError(t, err) - return e -} - func AddLanes(t *testing.T, e deployment.Environment, state changeset.CCIPOnChainState, pairs []changeset.SourceDestPair) deployment.Environment { addLanesCfg, commitOCR2Configs, execOCR2Configs, jobspecs := LaneConfigsForChains(t, e, state, pairs) var err error diff --git a/integration-tests/smoke/ccip/ccip_legacy_test.go b/integration-tests/smoke/ccip/ccip_legacy_test.go index b18957dbeee..2b5b6d77b58 100644 --- a/integration-tests/smoke/ccip/ccip_legacy_test.go +++ b/integration-tests/smoke/ccip/ccip_legacy_test.go @@ -15,7 +15,7 @@ import ( // This test does not run in CI, it is only written as an example of how to write a test for the legacy CCIP func TestE2ELegacy(t *testing.T) { - e := testsetups.NewIntegrationLegacyEnvironment(t) + e, _ := testsetups.NewIntegrationEnvironment(t, changeset.WithLegacyDeployment()) state, err := changeset.LoadOnchainState(e.Env) require.NoError(t, err) allChains := e.Env.AllChainSelectors() diff --git a/integration-tests/testsetups/ccip/test_helpers.go b/integration-tests/testsetups/ccip/test_helpers.go index d1aaca94027..6725ac1df9b 100644 --- a/integration-tests/testsetups/ccip/test_helpers.go +++ b/integration-tests/testsetups/ccip/test_helpers.go @@ -27,7 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_5" integrationnodes "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" corechainlink "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -138,29 +137,6 @@ func (l *DeployedLocalDevEnvironment) RestartChainlinkNodes(t *testing.T) error return errGrp.Wait() } -func NewIntegrationLegacyEnvironment(t *testing.T, opts ...changeset.TestOps) changeset.DeployedEnv { - testCfg := changeset.DefaultTestConfigs() - for _, opt := range opts { - opt(testCfg) - } - // check for EnvType env var - testCfg.MustSetEnvTypeOrDefault(t) - require.NoError(t, testCfg.Validate(), "invalid test config") - switch testCfg.Type { - case changeset.Memory: - memEnv := v1_5.NewMemoryEnvironment(t, opts...) - return memEnv - case changeset.Docker: - dockerEnv := &DeployedLocalDevEnvironment{} - deployedEnv := v1_5.NewEnvironment(t, testCfg, dockerEnv) - require.NotNil(t, dockerEnv.testEnv, "empty docker environment") - return deployedEnv - default: - require.Failf(t, "Type %s not supported in integration tests choose between %s and %s", string(testCfg.Type), changeset.Memory, changeset.Docker) - } - return changeset.DeployedEnv{} -} - // NewIntegrationEnvironment creates a new integration test environment based on the provided test config // It can create a memory environment or a docker environment based on env var CCIP_V16_TEST_ENV // By default, it creates a memory environment if env var CCIP_V16_TEST_ENV is not set @@ -181,6 +157,11 @@ func NewIntegrationEnvironment(t *testing.T, opts ...changeset.TestOps) (changes return memEnv, devenv.RMNCluster{} case changeset.Docker: dockerEnv := &DeployedLocalDevEnvironment{} + if testCfg.LegacyDeployment { + deployedEnv := changeset.NewLegacyEnvironment(t, testCfg, dockerEnv) + require.NotNil(t, dockerEnv.testEnv, "empty docker environment") + return deployedEnv, devenv.RMNCluster{} + } if testCfg.RMNEnabled { deployedEnv := changeset.NewEnvironmentWithJobsAndContracts(t, testCfg, dockerEnv) l := logging.GetTestLogger(t)