Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CCIP-4158 chain contract changeset #15294

Merged
merged 7 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions deployment/ccip/changeset/deploy_chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package changeset

import (
"fmt"

"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"

"github.com/smartcontractkit/chainlink/deployment"
ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip"
)

var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContracts

func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig) (deployment.ChangesetOutput, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments on these would be nice

newAddresses := deployment.NewMemoryAddressBook()
err := ccipdeployment.DeployChainContractsForChains(env, newAddresses, c.HomeChainSelector, c.ChainSelectors, c.MCMSCfg)
if err != nil {
env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "newAddresses", newAddresses)
return deployment.ChangesetOutput{AddressBook: newAddresses}, deployment.MaybeDataErr(err)
}
return deployment.ChangesetOutput{
Proposals: []timelock.MCMSWithTimelockProposal{},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all of these changesets going to be converted to use MCMS? Or is MCMS for post-deployment processes only (transferring ownership, setting config, etc.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MCMS for post deployment

AddressBook: newAddresses,
JobSpecs: nil,
}, nil
}

type DeployChainContractsConfig struct {
ChainSelectors []uint64
HomeChainSelector uint64
MCMSCfg ccipdeployment.MCMSConfig
}

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)
}
}
if err := deployment.IsValidChainSelector(c.HomeChainSelector); err != nil {
return fmt.Errorf("invalid home chain selector: %d - %w", c.HomeChainSelector, err)
}
return nil
}
78 changes: 78 additions & 0 deletions deployment/ccip/changeset/deploy_chain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package changeset

import (
"testing"

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

"github.com/smartcontractkit/chainlink/deployment"
ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip"
"github.com/smartcontractkit/chainlink/deployment/environment/memory"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)

func TestDeployChainContractsChangeset(t *testing.T) {
lggr := logger.TestLogger(t)
e := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{
Bootstraps: 1,
Chains: 2,
Nodes: 4,
})
selectors := e.AllChainSelectors()
homeChainSel := selectors[0]
nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain)
require.NoError(t, err)
p2pIds := nodes.NonBootstraps().PeerIDs()
// deploy home chain
homeChainCfg := DeployHomeChainConfig{
HomeChainSel: homeChainSel,
RMNStaticConfig: ccdeploy.NewTestRMNStaticConfig(),
RMNDynamicConfig: ccdeploy.NewTestRMNDynamicConfig(),
NodeOperators: ccdeploy.NewTestNodeOperator(e.Chains[homeChainSel].DeployerKey.From),
NodeP2PIDsPerNodeOpAdmin: map[string][][32]byte{
"NodeOperator": p2pIds,
},
}
output, err := DeployHomeChain(e, homeChainCfg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Figuring out the correct order of incantations to get a functioning environment seems to be getting harder with all these new changesets, are we planning a more "hard to mess up" API in order to do things like this or nah?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe for tests, a simple function that composes everything needed in order to get a functioning env

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a simple function that composes everything needed in order to get a functioning env

I added this in #15288 FYI

require.NoError(t, err)
require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook))

// deploy pre-requisites
prerequisites, err := DeployPrerequisites(e, DeployPrerequisiteConfig{
ChainSelectors: selectors,
})
require.NoError(t, err)
require.NoError(t, e.ExistingAddresses.Merge(prerequisites.AddressBook))

// deploy ccip chain contracts
output, err = DeployChainContracts(e, DeployChainContractsConfig{
ChainSelectors: selectors,
HomeChainSelector: homeChainSel,
MCMSCfg: ccdeploy.NewTestMCMSConfig(t, e),
})
require.NoError(t, err)
require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook))

// load onchain state
state, err := ccdeploy.LoadOnchainState(e)
require.NoError(t, err)

// verify all contracts populated
require.NotNil(t, state.Chains[homeChainSel].CapabilityRegistry)
require.NotNil(t, state.Chains[homeChainSel].CCIPHome)
require.NotNil(t, state.Chains[homeChainSel].RMNHome)
for _, sel := range selectors {
require.NotNil(t, state.Chains[sel].LinkToken)
require.NotNil(t, state.Chains[sel].Weth9)
require.NotNil(t, state.Chains[sel].TokenAdminRegistry)
require.NotNil(t, state.Chains[sel].RegistryModule)
require.NotNil(t, state.Chains[sel].Router)
require.NotNil(t, state.Chains[sel].RMNRemote)
require.NotNil(t, state.Chains[sel].TestRouter)
require.NotNil(t, state.Chains[sel].NonceManager)
require.NotNil(t, state.Chains[sel].FeeQuoter)
require.NotNil(t, state.Chains[sel].OffRamp)
require.NotNil(t, state.Chains[sel].OnRamp)
}
}
4 changes: 3 additions & 1 deletion deployment/ccip/changeset/home_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ func DeployHomeChain(env deployment.Environment, cfg DeployHomeChainConfig) (dep
_, err = ccipdeployment.DeployHomeChain(env.Logger, env, ab, env.Chains[cfg.HomeChainSel], cfg.RMNStaticConfig, cfg.RMNDynamicConfig, cfg.NodeOperators, cfg.NodeP2PIDsPerNodeOpAdmin)
if err != nil {
env.Logger.Errorw("Failed to deploy cap reg", "err", err, "addresses", env.ExistingAddresses)
return deployment.ChangesetOutput{}, err
return deployment.ChangesetOutput{
AddressBook: ab,
}, err
}

return deployment.ChangesetOutput{
Expand Down
21 changes: 21 additions & 0 deletions deployment/ccip/changeset/jobspec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package changeset

import (
"github.com/pkg/errors"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"

"github.com/smartcontractkit/chainlink/deployment"
ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip"
)

func Jobspec(env deployment.Environment, _ any) (deployment.ChangesetOutput, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment + how this is expected to be used in conjunction with all the other stuff would be useful

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also naming is kinda not great on this one

js, err := ccipdeployment.NewCCIPJobSpecs(env.NodeIDs, env.Offchain)
if err != nil {
return deployment.ChangesetOutput{}, errors.Wrapf(err, "failed to create job specs")
}
return deployment.ChangesetOutput{
Proposals: []timelock.MCMSWithTimelockProposal{},
AddressBook: deployment.NewMemoryAddressBook(),
JobSpecs: js,
}, nil
}
35 changes: 35 additions & 0 deletions deployment/ccip/changeset/jobspec_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package changeset

import (
"testing"

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

"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/environment/memory"
ccip "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/validate"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)

func TestJobSpecChangeset(t *testing.T) {
lggr := logger.TestLogger(t)
e := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{
Chains: 1,
Nodes: 4,
})
output, err := Jobspec(e, nil)
require.NoError(t, err)
require.NotNil(t, output.JobSpecs)
nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain)
require.NoError(t, err)
for _, node := range nodes {
jobs, exists := output.JobSpecs[node.NodeID]
require.True(t, exists)
require.NotNil(t, jobs)
for _, job := range jobs {
_, err = ccip.ValidatedCCIPSpec(job)
require.NoError(t, err)
}
}
}
12 changes: 4 additions & 8 deletions deployment/ccip/changeset/prerequisites.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
chain_selectors "github.com/smartcontractkit/chain-selectors"

"github.com/smartcontractkit/chainlink/deployment"
ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip"
Expand All @@ -27,7 +26,9 @@ func DeployPrerequisites(env deployment.Environment, cfg DeployPrerequisiteConfi
err = ccipdeployment.DeployPrerequisiteChainContracts(env, ab, cfg.ChainSelectors)
if err != nil {
env.Logger.Errorw("Failed to deploy prerequisite contracts", "err", err, "addressBook", ab)
return deployment.ChangesetOutput{}, fmt.Errorf("failed to deploy prerequisite contracts: %w", err)
return deployment.ChangesetOutput{
AddressBook: ab,
}, fmt.Errorf("failed to deploy prerequisite contracts: %w", err)
}
return deployment.ChangesetOutput{
Proposals: []timelock.MCMSWithTimelockProposal{},
Expand All @@ -45,14 +46,9 @@ type DeployPrerequisiteConfig struct {

func (c DeployPrerequisiteConfig) Validate() error {
for _, cs := range c.ChainSelectors {
if cs == 0 {
return fmt.Errorf("chain selector must be set")
}
_, err := chain_selectors.ChainIdFromSelector(cs)
if err != nil {
if err := deployment.IsValidChainSelector(cs); err != nil {
return fmt.Errorf("invalid chain selector: %d - %w", cs, err)
}

}
return nil
}
7 changes: 1 addition & 6 deletions deployment/ccip/changeset/save_existing.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
chain_selectors "github.com/smartcontractkit/chain-selectors"

"github.com/smartcontractkit/chainlink/deployment"
)
Expand All @@ -27,11 +26,7 @@ type ExistingContractsConfig struct {

func (cfg ExistingContractsConfig) Validate() error {
for _, ec := range cfg.ExistingContracts {
if ec.ChainSelector == 0 {
return fmt.Errorf("chain selectors must be set")
}
_, err := chain_selectors.ChainIdFromSelector(ec.ChainSelector)
if err != nil {
if err := deployment.IsValidChainSelector(ec.ChainSelector); err != nil {
return fmt.Errorf("invalid chain selector: %d - %w", ec.ChainSelector, err)
}
if ec.Address == (common.Address{}) {
Expand Down
101 changes: 67 additions & 34 deletions deployment/ccip/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,29 +321,11 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c
e.Logger.Errorw("Failed to get capability registry")
return fmt.Errorf("capability registry not found")
}
cr, err := capReg.GetHashedCapabilityId(
&bind.CallOpts{}, CapabilityLabelledName, CapabilityVersion)
if err != nil {
e.Logger.Errorw("Failed to get hashed capability id", "err", err)
return err
ccipHome := existingState.Chains[c.HomeChainSel].CCIPHome
if ccipHome == nil {
e.Logger.Errorw("Failed to get ccip home", "err", err)
return fmt.Errorf("ccip home not found")
}
if cr != CCIPCapabilityID {
return fmt.Errorf("capability registry does not support CCIP %s %s", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityID[:]))
}
capability, err := capReg.GetCapability(nil, CCIPCapabilityID)
if err != nil {
e.Logger.Errorw("Failed to get capability", "err", err)
return err
}
ccipHome, err := ccip_home.NewCCIPHome(capability.ConfigurationContract, e.Chains[c.HomeChainSel].Client)
if err != nil {
e.Logger.Errorw("Failed to get ccip config", "err", err)
return err
}
if ccipHome.Address() != existingState.Chains[c.HomeChainSel].CCIPHome.Address() {
return fmt.Errorf("ccip home address mismatch")
}

rmnHome := existingState.Chains[c.HomeChainSel].RMNHome
if rmnHome == nil {
e.Logger.Errorw("Failed to get rmn home", "err", err)
Expand All @@ -352,18 +334,10 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c

usdcConfiguration := make(map[cciptypes.ChainSelector]pluginconfig.USDCCCTPTokenConfig)
for _, chainSel := range c.ChainsToDeploy {
chain, ok := e.Chains[chainSel]
if !ok {
chain, exists := e.Chains[chainSel]
if !exists {
return fmt.Errorf("chain %d not found", chainSel)
}
if existingState.Chains[chainSel].LinkToken == nil || existingState.Chains[chainSel].Weth9 == nil {
return fmt.Errorf("fee tokens not found for chain %d", chainSel)
}
err = DeployChainContracts(e, chain, ab, c.MCMSConfig, rmnHome)
if err != nil {
return err
}

if c.USDCConfig.Enabled {
token, pool, messenger, transmitter, err1 := DeployUSDC(e.Logger, chain, ab, existingState.Chains[chainSel])
if err1 != nil {
Expand All @@ -383,10 +357,13 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c
}
}
}

err = DeployChainContractsForChains(e, ab, c.HomeChainSel, c.ChainsToDeploy, c.MCMSConfig)
if err != nil {
e.Logger.Errorw("Failed to deploy chain contracts", "err", err)
return err
}
for _, chainSel := range c.ChainsToDeploy {
chain, _ := e.Chains[chainSel]

chainAddresses, err := ab.AddressesForChain(chain.Selector)
if err != nil {
e.Logger.Errorw("Failed to get chain addresses", "err", err)
Expand Down Expand Up @@ -553,6 +530,62 @@ func DeployMCMSContracts(
}, nil
}

func DeployChainContractsForChains(e deployment.Environment, ab deployment.AddressBook, homeChainSel uint64, chainsToDeploy []uint64, mcmsConfig MCMSConfig) error {
existingState, err := LoadOnchainState(e)
if err != nil {
e.Logger.Errorw("Failed to load existing onchain state", "err")
return err
}

capReg := existingState.Chains[homeChainSel].CapabilityRegistry
if capReg == nil {
e.Logger.Errorw("Failed to get capability registry")
return fmt.Errorf("capability registry not found")
}
cr, err := capReg.GetHashedCapabilityId(
&bind.CallOpts{}, CapabilityLabelledName, CapabilityVersion)
if err != nil {
e.Logger.Errorw("Failed to get hashed capability id", "err", err)
return err
}
if cr != CCIPCapabilityID {
return fmt.Errorf("capability registry does not support CCIP %s %s", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityID[:]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("capability registry does not support CCIP %s %s", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityID[:]))
return fmt.Errorf("unexpected mismatch between calculated ccip capability id (%s) and expected ccip capability id constant (%s)", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityID[:]))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would ultimately be a bug in the constant

}
capability, err := capReg.GetCapability(nil, CCIPCapabilityID)
if err != nil {
e.Logger.Errorw("Failed to get capability", "err", err)
return err
}
ccipHome, err := ccip_home.NewCCIPHome(capability.ConfigurationContract, e.Chains[homeChainSel].Client)
if err != nil {
e.Logger.Errorw("Failed to get ccip config", "err", err)
return err
}
if ccipHome.Address() != existingState.Chains[homeChainSel].CCIPHome.Address() {
return fmt.Errorf("ccip home address mismatch")
}
rmnHome := existingState.Chains[homeChainSel].RMNHome
if rmnHome == nil {
e.Logger.Errorw("Failed to get rmn home", "err", err)
return fmt.Errorf("rmn home not found")
}
for _, chainSel := range chainsToDeploy {
chain, ok := e.Chains[chainSel]
if !ok {
return fmt.Errorf("chain %d not found", chainSel)
}
if existingState.Chains[chainSel].LinkToken == nil || existingState.Chains[chainSel].Weth9 == nil {
return fmt.Errorf("fee tokens not found for chain %d", chainSel)
}
err := DeployChainContracts(e, chain, ab, mcmsConfig, rmnHome)
if err != nil {
e.Logger.Errorw("Failed to deploy chain contracts", "chain", chainSel, "err", err)
return fmt.Errorf("failed to deploy chain contracts for chain %d: %w", chainSel, err)
}
}
return nil
}

func DeployChainContracts(
e deployment.Environment,
chain deployment.Chain,
Expand Down
Loading
Loading